1
FUNCTIONAL PROGRAMMING
Graham Hutton
Lecture 9 - Interactive Programs
1
Introduction
To date, we have seen how Haskell can be used to write batch programs that take all their inputs at the start and give all their outputs at the end.
batch program
inputs outputs
2
However, we would also like to use Haskell to write interactive programs that read from the keyboard and write to the screen, as they are running.
interactive program
inputs outputs
keyboard
screen
3
The Problem
Haskell programs are pure mathematical functions:
However, reading from the keyboard and writing to the screen are side effects:
Haskell programs have no side effects.
Interactive programs have side effects.
4
The Solution
Interactive programs can be written in Haskell by using types to distinguish pure expressions from impure actions that may involve side effects.
IO a
The type of actions that return a value of type a.
5
For example:
IO Char
IO ()
The type of actions that return a character.
The type of purely side effecting actions that return no result value.
() is the type of tuples with no components.
Note:
2
6
Primitive Actions
The standard library provides a number of actions, including the following three primitives:
getChar :: IO Char
The action getChar reads a character from the keyboard, echoes it to the screen, and returns the character as its result value:
7
The action putChar c writes the character c to the screen, and returns no result value:
putChar :: Char → IO ()
The action return v simply returns the value v, without performing any interaction:
return :: a → IO a
8
A sequence of actions can be combined as a single composite action using the keyword do.
For example:
Sequencing Actions
getTwo :: IO (Char,Char) getTwo = do x ← getChar
y ← getChar return (x,y)
9
Note - in a sequence of actions:
Each action must begin in precisely the same column. That is, the layout rule applies.
The values returned by intermediate actions are discarded by default, but if required can be named using the ← operator.
The value returned by the last action is the value returned by the sequence as a whole.
10
Other Library Actions
getLine :: IO String getLine = do x ← getChar
if x == '\n' then return []
else
do xs ← getLine return (x:xs)
Reading a string from the keyboard:
11
putStr :: String → IO () putStr [] = return () putStr (x:xs) = do putChar x
putStr xs
Writing a string to the screen:
Writing a string and moving to a new line:
putStrLn :: String → IO () putStrLn xs = do putStr xs
putChar '\n'
3
12
Example
We can now define an action that prompts for a string to be entered and displays its length:
strlen :: IO ()
strlen = do putStr "Enter a string: "
xs ← getLine
putStr "The string has "
putStr (show (length xs)) putStrLn " characters"
13
For example:
> strlen
Enter a string: hello there The string has 11 characters
Evaluating an action executes its side effects, with the final result value being discarded.
Note:
14
Hangman
Consider the following version of hangman:
One player secretly types in a word.
The other player tries to deduce the word, by entering a sequence of guesses.
For each guess, the computer indicates which letters in the secret word occur in the guess.
15
The game ends when the guess is correct.
hangman :: IO () hangman =
do putStrLn "Think of a word: "
word ← sgetLine
putStrLn "Try to guess it:"
guess word
We adopt a top down approach to implementing hangman in Haskell, starting as follows:
16
The action sgetLine reads a line of text from the keyboard, echoing each character as a dash:
sgetLine :: IO String sgetLine = do x ← getCh
if x == '\n' then do putChar x
return []
else
do putChar '-' xs ← sgetLine return (x:xs)
17
primitive getCh :: IO Char Note:
The action getCh reads a character from the keyboard, without echoing it to the screen.
This useful action is not part of the standard library, but is a special Hugs primitive that can be imported into a script as follows:
4
18
The function guess is the main loop, which requests and processes guesses until the game ends.
guess :: String → IO () guess word =
do putStr "> "
xs ← getLine if xs == word then
putStrLn "You got it!"
else
do putStrLn (diff word xs) guess word
19
The function diff indicates which characters in one string occur in a second string:
For example:
> diff "haskell" "pascal"
"-as--ll"
diff :: String → String → String diff xs ys =
[if elem x ys then x else '-' | x ← xs]
20
If you would like to try out the hangman game for yourself, it is available on our Unix machines:
% hugs ~gmh/Teaching/FUN/Source/hangman.hs
> hangman
Think of a word:
---
Try to guess it:
> pascal -as--ll
> haskell You got it!!
21
Exercise
Implement the game of nim in Haskell, where the rules of the game are as follows:
The board comprises five rows of stars:
1: * * * * * 2: * * * * 3: * * * 4: * * 5: *
22
Two players take it turn about to remove one or more stars from the end of a single row.
The winner is the player who removes the last star or stars from the board.
Hint:
Represent the board as a list of five integers that give the number of stars remaining on each row.
For example, the initial board is [5,4,3,2,1].
)81&7,21$/352*5$00,1*
*UDKDP+XWWRQ
/HFWXUH'HILQLQJ7\SHV
7\SH'HFODUDWLRQV
,Q+DVNHOODQHZQDPHIRUDQH[LVWLQJW\SHFDQEH GHILQHGXVLQJDW\SHGHFODUDWLRQ
W\SH6WULQJ >&KDU@
6WULQJLVDV\QRQ\PIRUWKHW\SH>&KDU@
7\SHGHFODUDWLRQVFDQEHXVHGWRPDNHRWKHUW\SHV HDVLHUWRUHDG)RUH[DPSOHJLYHQ
RULJLQ3RV RULJLQ
OHIW3RV→3RV OHIW[\ [\
W\SH3RV ,QW,QW ZHFDQGHILQH
/LNHIXQFWLRQGHILQLWLRQVW\SHGHFODUDWLRQVFDQDOVR KDYHSDUDPHWHUV)RUH[DPSOHJLYHQ
W\SH3DLUD DD
ZHFDQGHILQH
ELWV3DLU,QW ELWV
FRS\D→3DLUD FRS\[ [[
7\SHGHFODUDWLRQVFDQEHQHVWHG
W\SH3RV ,QW,QW W\SH7UDQV 3RV→3RV
+RZHYHUWKH\FDQQRWEHUHFXUVLYH
W\SH7UHH ,QW>7UHH@
'DWD'HFODUDWLRQV
$QHZW\SHFDQEHGHILQHGE\VSHFLI\LQJLWVVHWRI YDOXHVXVLQJDGDWDGHFODUDWLRQ
GDWD%RRO )DOVH_7UXH
%RROLVDQHZW\SHZLWKWZR QHZYDOXHV)DOVHDQG7UXH
1RWH
❚ 7KHWZRYDOXHV)DOVHDQG7UXHDUHFDOOHGWKH FRQVWUXFWRUVIRUWKHW\SH%RRO
❚ 7\SHDQGFRQVWUXFWRUQDPHVPXVWEHJLQZLWK DQXSSHUFDVHOHWWHU
❚ 'DWDGHFODUDWLRQVDUHVLPLODUWRFRQWH[WIUHH JUDPPDUV7KHIRUPHUVSHFLILHVWKHYDOXHVRI DW\SHWKHODWWHUWKHVHQWHQFHVRIDODQJXDJH
DQVZHUV>$QVZHU@
DQVZHUV ><HV1R8QNQRZQ@
IOLS$QVZHU→$QVZHU IOLS<HV 1R
IOLS1R <HV IOLS8QNQRZQ 8QNQRZQ
GDWD$QVZHU <HV_1R_8QNQRZQ ZHFDQGHILQH
9DOXHVRIQHZW\SHVFDQEHXVHGLQWKHVDPHZD\V DVWKRVHRIEXLOWLQW\SHV)RUH[DPSOHJLYHQ
7KHFRQVWUXFWRUVLQDGDWDGHFODUDWLRQFDQDOVRKDYH SDUDPHWHUV)RUH[DPSOHJLYHQ
GDWD6KDSH &LUFOH)ORDW
_5HFW)ORDW)ORDW
VTXDUH6KDSH VTXDUH 5HFW
DUHD6KDSH→)ORDW DUHD&LUFOHU SL UA
DUHD5HFW[\ [ \
ZHFDQGHILQH
1RWH
❚ 6KDSHKDVYDOXHVRIWKHIRUP&LUFOHUZKHUHULV DIORDWDQG5HFW[\ZKHUH[DQG\DUHIORDWV
❚ &LUFOHDQG5HFWFDQEHYLHZHGDVIXQFWLRQVWKDW VLPSO\FRQVWUXFWYDOXHVRIW\SH6KDSH
&LUFOH)ORDW→6KDSH
5HFW)ORDW→)ORDW→6KDSH
1RWVXUSULVLQJO\GDWDGHFODUDWLRQVWKHPVHOYHVFDQ DOVRKDYHSDUDPHWHUV)RUH[DPSOHJLYHQ
GDWD0D\EHD 1RWKLQJ_-XVWD
]HUR0D\EH,QW ]HUR -XVW
DSSD→E→0D\EHD→0D\EHE DSSI1RWKLQJ 1RWKLQJ
DSSI-XVW[ -XVWI[
ZHFDQGHILQH
5HFXUVLYH7\SHV
,Q+DVNHOOQHZW\SHVFDQEHGHILQHGLQWHUPVRI WKHPVHOYHV7KDWLVW\SHVFDQEHUHFXUVLYH
GDWD1DW =HUR_6XFF1DW
1DWLVDQHZW\SHZLWKFRQVWUXFWRUV
=HUR1DWDQG6XFF1DW→1DW
1RWH
❚ $YDOXHRIW\SH1DWLVHLWKHU=HURRURIWKHIRUP 6XFFQZKHUHQ1DW7KDWLV1DWFRQWDLQVWKH IROORZLQJLQILQLWHVHTXHQFHRIYDOXHV
=HUR 6XFF=HUR
6XFF6XFF=HUR
••
•
❚ :HFDQWKLQNRIYDOXHVRIW\SH1DWDVQDWXUDO QXPEHUVZKHUH=HURUHSUHVHQWVDQG6XFF UHSUHVHQWVWKHVXFFHVVRUIXQFWLRQ
❚ )RUH[DPSOHWKHYDOXH
6XFF6XFF6XFF=HUR UHSUHVHQWVWKHQDWXUDOQXPEHU
8VLQJUHFXUVLRQLWLVHDV\WRGHILQHIXQFWLRQVWKDW FRQYHUWEHWZHHQYDOXHVRIW\SH1DWDQG,QW
QDWLQW1DW→,QW QDWLQW=HUR
QDWLQW6XFFQ QDWLQWQ
LQWQDW,QW→1DW LQWQDW =HUR
LQWQDWQ 6XFFLQWQDWQ
7ZRQDWXUDOVFDQEHDGGHGE\FRQYHUWLQJWKHPWR LQWHJHUVDGGLQJDQGWKHQFRQYHUWLQJEDFN
+RZHYHUXVLQJUHFXUVLRQWKHIXQFWLRQDGGFDQEH GHILQHGZLWKRXWWKHQHHGIRUFRQYHUVLRQV
DGG1DW→1DW→1DW
DGGPQ LQWQDWQDWLQWPQDWLQWQ
DGG=HURQ Q
DGG6XFFPQ 6XFFDGGPQ
)RUH[DPSOH
DGG6XFF6XFF=HUR6XFF=HUR 6XFFDGG6XFF=HUR6XFF=HUR 6XFF6XFFDGG=HUR6XFF=HUR 6XFF6XFF6XFF=HUR
1RWH
❚ 7KHUHFXUVLYHGHILQLWLRQIRUDGGFRUUHVSRQGVWR WKHODZVQ QDQGPQ PQ
$ULWKPHWLF([SUHVVLRQV
&RQVLGHUDVLPSOHIRUPRIH[SUHVVLRQVEXLOWXSIURP LQWHJHUVXVLQJDGGLWLRQDQGPXOWLSOLFDWLRQ
∗
8VLQJUHFXUVLRQDVXLWDEOHQHZW\SHWRUHSUHVHQW VXFKH[SUHVVLRQVFDQEHGHILQHGE\
)RUH[DPSOHWKHH[SUHVVLRQRQWKHSUHYLRXVVOLGH ZRXOGEHUHSUHVHQWHGDVIROORZV
GDWD([SU 9DO,QW
_$GG([SU([SU
_0XO([SU([SU
$GG9DO0XO9DO9DO
8VLQJUHFXUVLRQLWLVQRZHDV\WRGHILQHIXQFWLRQV WKDWSURFHVVH[SUHVVLRQV)RUH[DPSOH
VL]H([SU→,QW VL]H9DOQ
VL]H$GG[\ VL]H[VL]H\
VL]H0XO[\ VL]H[VL]H\
HYDO([SU→,QW HYDO9DOQ Q
HYDO$GG[\ HYDO[HYDO\
HYDO0XO[\ HYDO[ HYDO\
1RWH
❚ 7KHWKUHHFRQVWUXFWRUVKDYHW\SHV
9DO,QW→([SU
$GG([SU→([SU→([SU 0XO([SU→([SU→([SU
❚ 0DQ\IXQFWLRQVRQH[SUHVVLRQVFDQEHGHILQHG E\UHSODFLQJWKHFRQVWUXFWRUVE\RWKHUIXQFWLRQV XVLQJDVXLWDEOHIROGIXQFWLRQ)RUH[DPSOH
HYDO IROGLG
%LQDU\7UHHV
,QFRPSXWLQJLWLVRIWHQXVHIXOWRVWRUHGDWDLQD WZRZD\EUDQFKLQJVWUXFWXUHRUELQDU\WUHH
8VLQJUHFXUVLRQDVXLWDEOHQHZW\SHWRUHSUHVHQW VXFKELQDU\WUHHVFDQEHGHILQHGE\
)RUH[DPSOHWKHWUHHRQWKHSUHYLRXVVOLGHZRXOG EHUHSUHVHQWHGDVIROORZV
GDWD7UHH /HDI,QW
_1RGH7UHH,QW7UHH
1RGH1RGH/HDI/HDI
1RGH/HDI/HDI
:HFDQGHILQHDIXQFWLRQILQGWKDWGHFLGHVLIDJLYHQ LQWHJHURFFXUVLQDELQDU\WUHH
ILQG,QW→7UHH→%RRO ILQG[/HDIQ [ Q
ILQG[1RGHOQU [ Q
__ILQG[O
__ILQG[U +RZHYHUWKLVIXQFWLRQVLPSO\WUDYHUVHVWKHHQWLUH WUHHDQGKHQFHIRURXUH[DPSOHWUHHPD\UHTXLUH XSWRVHYHQFRPSDULVRQVWRSURGXFHDUHVXOW
1RZFRQVLGHUWKHIXQFWLRQIODWWHQWKDWUHWXUQVWKH OLVWRIDOOWKHLQWHJHUVFRQWDLQHGLQDWUHH
IODWWHQ7UHH→>,QW@
IODWWHQ/HDIQ >Q@
IODWWHQ1RGHOQU IODWWHQO
>Q@
IODWWHQU
$WUHHLVDVHDUFKWUHHLILWIODWWHQVWRDOLVWWKDWLV RUGHUHG2XUH[DPSOHWUHHLVDVHDUFKWUHHDVLW IODWWHQVWRWKHRUGHUHGOLVW>@
6HDUFKWUHHVKDYHWKHLPSRUWDQWSURSHUW\WKDWZKHQ WU\LQJWRILQGDYDOXHLQDWUHHZHFDQDOZD\VGHFLGH ZKLFKRIWKHWZRVXEWUHHVLWPD\RFFXULQ
)RUH[DPSOHWU\LQJWRILQGDQ\YDOXHLQRXUVHDUFK WUHHRQO\WDNHVDWPRVWWKUHHFRPSDULVRQV
ILQG[/HDIQ [ Q ILQG[1RGHOQU_[ Q 7UXH
_[Q ILQG[O
_[!Q ILQG[U
([HUFLVHV
8VLQJUHFXUVLRQDQGWKHIXQFWLRQDGGGHILQHD IXQFWLRQWKDWPXOWLSOLHVWZRQDWXUDOQXPEHUV
'HILQHDVXLWDEOHIXQFWLRQIROGIRUH[SUHVVLRQV
DQGJLYHDIHZH[DPSOHVRILWVXVH
$ELQDU\WUHHLVFRPSOHWHLIWKHWZRVXEWUHHVRI HYHU\QRGHDUHRIHTXDOVL]H'HILQHDIXQFWLRQ WKDWGHFLGHVLIDELQDU\WUHHLVFRPSOHWH
1 FFF FU U U UN N N NC C C CTT TIIIIO T ON O O NA N N ALLL A A L
P P P
PR R R RO O O OG G G GR RA R R AM A A MM M M M M MIIIIN N N NG G G G
Bernhard Reus
Lecture 11 - Basic Graphics
1
The Hugs Graphics Library
❚ GraphicsUtils.hs works within Hugs
❚ Written by Alastair Reid, University of Utah
❚ Library compatible with Win32 and X11
❚ Graphics as Input/Output
❚ Embedded in Haskell IO type
2
A First Example
helloWorld :: IO ()
helloWorld = runGraphics (
do w <- openWindow "Hello World Window"
(300,300)
drawInWindow w (text (100,100) "Hello") drawInWindow w (text (100,200) "World") getKey w
closeWindow w )
Open a window, print "Hello World" in it and close the window as soon as the user releases a key.
3
Example on Screen
4
❚ As graphics are IO one can use the do syntax for sequences of Graphic actions
runGraphics :: IO () -> IO ()
prepares Hugs for graphics, runs argument and cleans up.
opens a window with given title and size and returns it
openWindow :: Title -> Size -> IO Window
5
text :: Point -> String -> Graphic
drawInWindow :: Window -> Graphic -> IO ()
produces a graphic that consists of the given string printed at given coordinates.
Point specifies top-left corner draws a graphic object in a given window
2
6
Size and Point are types defined as follows type Size = (Int,Int)
type Point = (Int,Int)
waits for user to press and release a key and returns corresponding character getKey :: Window -> IO Char
width and height x and y-coordinates
closeWindow :: Window -> IO () closes given window
7
Graphic Library Overview
❚ Window: draw in it a Graphic, check for Events.
❚ Graphic: built by drawing figures or text:
line, polygon, polyline, ellipse, arc or text.
Graphics can be stacked with overGraphic.
❚ Graphic modifiers to change
Color, Pen, Brush, Font, background.
❚ Event handling: events are
Char, Key, MouseMove, Button (of mouse).
8
Overview (cont’d)
❚ Event handling explicit and no Listeners as in Java.
❚ No complex container objects as in Java API.
❚ For more detailed information consult
"The Hugs Graphics Library" document on the course webpage.
❚ Do not forget to import GraphicsUtils before use.
9
openWindow :: Title -> Size -> IO Window closeWindow :: Window -> IO ()
clearWindow :: Window -> IO () getGraphic :: Window -> IO Graphic setGraphic :: Window -> Graphic -> IO () drawInWindow :: Window -> Graphic -> IO ()
Windows
open, close, and clearWindow do the obvious.
getGraphic returns the graphic of a window and setGraphic prints a graphic (overriding).
drawInWindow prints over the given graphic.
10
Windows (cont’d)
getWindowRect :: Window -> IO (Point,Point)
getWindowEvent :: Window -> IO Event gets window parameters: location and size as points. Useful when the window has been resized or moved.
gets next window event
11
Atomic Graphics
text :: Point -> String -> Graphic
line :: Point -> Point -> Graphic draws a given string at a given point
draws a line between two points polygon :: [Point] -> Graphic
draws a filled polygon through points in list
3
12
Atomic Graphics
ellipse :: Point -> Point -> Graphic draws an unfilled elliptical arc
in rectangle defined by two points with two angles (in degrees) as shown beneath:
draws a filled ellipse inside given rectangle arc :: Point -> Point -> Angle -> Angle
-> Graphic
angle2 angle1
13
Graphics & Modifiers
withColor :: Color -> Graphic -> Graphic puts first graphic on top of second
produces that graphic in given colour
overGraphic :: Graphic -> Graphic -> Graphic
withTextAlignment :: Alignment -> Graphic
-> Graphic
produces text in graphic in given alignment
14
Modifier Types
type Alignment = (HAlign,VAlign) type HAlign = Left’ | Center | Right’
type VAlign = Top | Baseline | Bottom Colour names
type Color =
Black | Blue | Green | Cyan | Red | Magenta | Yellow | White
Alignment names. Default = (Left’,Top)
15
Example
hw :: IO () hw = runGraphics (
do w <- openWindow "Hello World Window"
(300,300)
drawInWindow w (text (100,100) "Hello") drawInWindow w (withColor Blue
(text (100,200) "World")) drawInWindow w (withColor Magenta
(polygon [(100,60),(70,50),(100,20)])) getKey w
closeWindow w )
16
Example Comments
❚ “Hello World” with “World” in blue
❚ Magenta polygon with three nodes
= triangle
17
Events: how to get them
❚ In a nonterminating loop trace all events in a window and print them on the screen:
tracer:: IO ()
tracer = runGraphics (
do w <- openWindow "Event Tracer"
(300,300) loop w )
where loop w = do e <- getWindowEvent w putStrLn (show e) loop w
4
18
❚ Close the window by using the window menu or typing <ALT><F4>.
❚ show also works for events and transforms them into strings.
❚ We can mix graphic actions with our old IO actions as they are both of type IO a .
19
Event types
❚ Boolean parameters:
Key : is key down? (or released)
Button: is left (or right) mouse button pressed?
is button down? (or released) data Event =
Char Char
| Key Key Bool
| Button Point Bool Bool
| MouseMove Point
| Resize
| Closed
Meaning Character pressed key pressed
mouse button pressed mouse moved to point window resized window closed
20
Key Event types
❚ Note that types and constructors may have the same name (Char, Key)
❚ Special keys can be checked using predicates like: isDeleteKey :: Key -> Bool
isEscapeKey :: Key -> Bool isShiftLKey :: Key -> Bool isShiftRKey :: Key -> Bool isControlLKey :: Key -> Bool isControlRKey :: Key -> Bool … and so on.
21
Event Example
A simple program that prints red crosses (Xs) where you move the mouse.
If a character key is hit the character is printed in yellow in the middle of the window. Hitting the Escape-key closes the window.
22
crazy :: IO () crazy = runGraphics (
do w <- openWindow "Move Mouse" (300,300) loop w )
loop :: Window -> IO ()
loop w = do e <- getWindowEvent w handleEvent w e
❚ Open the window and start the event loop.
❚ The handler is a separate function (could be made local using where).
23
Events: can you handle them?
handleEvent :: Window -> Event -> IO () handleEvent w (MouseMove (x,y)) = do drawInWindow w
(withColor Red (text (x,y) "X")) loop w
handleEvent w (Char c) = do drawInWindow w
(withColor Black (polygon [(150,150),(150,170), (170,170),(170,150)] )) drawInWindow w (withColor Yellow
(text (150,150) [c])) loop w
5
24
handleEvent w (Key a True) =
if isEscapeKey a
then closeWindow w else loop w handleEvent w _ = loop
❚ Pattern matching can be used for Event argument in an event handler definition.
❚ Deletion of text is done by drawing a rectangle in background colour.
❚ One has to “catch” also the events one is not interested in or the program will abort.
25
Exercises
Write a program that opens a window (of appropriate size) and draws a red square of length 100 pixels and a blue circle of radius 50 pixels next to each other.
Swap the colours of the graphics when a mouse button has been pressed and close the window when the left Shift key has been pressed.
(1)
(2)
26
Write a program that opens a window, waits for the user to type printable characters on the keyboard and echoes them in the window (as long as there is space). If the left mouse button is pressed the content of the window is erased and printing starts again in the top left corner. The right button closes the window.
Assume that characters have width 9 pixels and height 14 pixels.
(3)
1 FUNCTIONAL PROGRAMMING
Bernhard Reus
Lecture 12 - Modules
1
Modules
!For bigger projects Haskell scripts should be structured and split into modules.
!A module consists of type, function, and object definitions with a clearly defined interface.
!Interfaces state what is exported and what is imported.
1
Advantages of Modules
!separate development
!separate compilation
!re-usability (libraries)
!reduce complexity
1
Module Headers
Syntax for module is as follows:
module <Name> where
<list of definitions>
!<Name>must start with an uppercase letter and is the module’s name. The file itself must be called <Name>.hs.
!The <list of definitions>contains definitions in the usual way. They describe the entities defined in the module.
1
module Gui where
type Point = (Int,Int)
movePoint:: Point -> Int -> Point movePoint (x,y) z = (x+z,y+z) For example, consider a file Gui.hs:
After loading Gui.hsHugs will change its prompt indicating that evaluation now is w.r.t. the definitions in module Gui.
Gui>
1
Import
The importclause allows one to import other modules’ definitions:
module Gui where import GraphicsUtils import MyOtherModule
data Shape = Rect Point Point
| Circle Int
drawShape:: Window -> Shape -> Graphic drawShape w (Rect p1 p2) = …
drawShape w (Circle n) = …
2
1
Import Caveats
!Obey layout rule with respect to import keyword.
!Avoid cyclic imports.
!Make sure that the .hs file containing your module has the right name (that is the same name).
1
Export Controls
module Gui (Shape (..),drawShape) where import GraphicsUtils
data Shape = … drawShape :: …
…
!By default a module exports all the definitions it contains but not the imported ones.
!This can be changed by explicitly providing an export list:
1
!The export list contains the names of the exported objects and types. The list is written in parentheses and names are separated by commas.
!Shape (..)indicates that the type Shapewith its constructors is exported.
!To export a whole module write:
module Gui (module GraphicsUtils) where…
1
!Therefore, the following two definitions are equivalent:
!Other example:
module Gui (module Gui) where…
module Gui where…
module Gui (module Gui, module Prelude) where…
1
Name Clashes
!If files are imported then there is always the danger of name clashes. For example,
!To avoid name clashes one can control the import list:
module A where import B f x = x + 3
module B where f xs = “foo” ++ xs g = map f
1
Import Controls
!Like export lists one can also define import lists explicitly. For example:
!Only the names in the list (in parentheses, separated by commas) are imported.
!In the definition of g, fstill refers to the fof B. module A where
import B (g) f x = x + 3
module B where f xs = “foo” ++ xs g = map f
3
1
Shadowing
!Hiding names that can be used for other definitions is also called shadowing.
!Keyword hidingallows one to shadow several names and import all the rest:
!Again, the list of names can contain types and names are separated by commas.
module A where import B hiding (f) f x = x + 3
1
Qualified Names
!What if one likes to use both functions named f in modules A and B? How can the name clash be resolved then?
!Use qualified names.
!A qualified name is built from the name of a module and the name of an object in that module. For example:
A.f B.f
1
Qualified Import
!Use keyword qualifiedto make imported names qualified. For example:
!Qualified import can be combined with all the other features of import lists.
module A where import qualified B
f x = x +3 g = map B.f
1
!In export lists there must not be any name clashes.
!Attention: A qualifier in a name identifies the module an entity is imported from, not the module it is defined in. For example:
!Name clash if one imports from Aas B.fand C.f are both referenced outside Aas A.f (not A.B.fand A.C.f).
module A (B.f, C.f) where import qualified B(f) import qualified C(f) …
1
!Modules must be closed. Any name mentioned in the module must be defined in the source or imported from another module.
!Instance declarations are exported and imported automatically.
1
Abstract Data Types
!Information Hiding.
!Do not give away implementation details to users of your module.
!Example: Sets.
If there is an implementation of sets by lists how can that implementation be hidden?
4
1
Abstract Data Type Set
!Use the export mechanism to hide information:
module Set (Set,empty,single,union,meet) where
data Set a = S [a]
empty = S []
single x = S [x]
union (S xs)(S ys) = S (elimDoubles (xs++ys)) meet (S xs)(S ys) =
S (elimDoubles [x| x <- xs, elem x ys])
1
!Note that the constructor Sfor type Setis not exported.
!Therefore, the only way to construct sets in another module is by using exported functions empty, single, union, and meet.
!Modules that provide the same export list are interchangeable (if the types of the entities match)
!No “implements” like in Java.
1
How to use Modules
!Modules should implement one specific task only and completely.
!Modules should be small.
!Modules should be as general as possible to support re-usability.
!Modules should only export what is absolutely necessary (information hiding).
1
Exercises
Put your solutions of previous exercise sheets in modules.
Complete the module Setgiving a definition for elimDoublesand write another module MySet that provides all the set functions where union is changed to have three arguments. All those functions but the binary union should be exported. Import them in a module C where you define some sets using union and single.
(1)
(2)
1
(3) Write an abstract data type Stack that uses lists as data representation, Implement the following stack operations:
emptyS:: Stack a
push :: a -> Stack a -> Stack a pop :: Stack a -> Stack a
1 FFF FU U U UN N N NC C C CTT TIIIIO T ON O O NA N N ALLL A A L
P P P
PR R R RO O O OG G G GR RA R R AM A A MM M M M M MIIIIN N N NG G G G
Bernhard Reus
Lecture 13 - Classes
1
Type Classes
❚ Type classes are a means for defining overloading in a clear and structured way.
❚ Type classes appear as constraints in types of overloaded functions.
❚ Several type classes are built-in, for example:
Ord, Eq, Num.
❚ This lecture explains how user-defined classes can be done and how they work.
2
Overloaded Functions
(==) :: Eq a => a -> a -> Bool (<=) :: Ord a => a -> a -> Bool (+) :: Num a => a -> a -> a show :: Show a => a -> String
Class constraint
Different implementations of overloaded functions provided in different types.
3
Class & Instance
❚ A class is the collection of types over which an overloaded function is defined.
❚ The members (types) of a class are called instances.
❚ For example, instances of Eq are: Int, Bool, String, Float, but also (Int,Char), [Char], [[Char]], [(Char,Char)], and so on.
Thus, (==) works for all instances of Eq.
4
How to Define a Class
A signature is a list of function names with their types. To write down these types, a type variable is used that represents the possible instances. For example:
class <Name> <TypeVar> where
<signature involving <TypeVar>>
class Eq a where (==) :: a -> a -> Bool
5
How to Define an Instance
The definitions of the functions refer now to the specified type that replaces the type variable in the class definition. Example:
instance <ClassName> <Type> where
<definition of functions in the signature of <ClassName>>
instance Eq Bool where True == True = True True == False = False False == True = False False == False = True
2
6
❚ For built-in numeric types like Float and Int etc., instance definitions refer to appropriate primitive equality definitions provided as native code by the compiler.
❚ For user defined classes functions are defined as usual (using pattern matching etc.).
❚ Classes can contain default implementations.
❚ Classes can depend on other classes and instances can depend on other instances.
7
Default Implementations
.
class Eq a where
(==), (/=) :: a -> a -> Bool x /= y = not (x == y)
Default implementations can be overridden by instance definitions.
8
Derived Classes
Class definitions may depend on other classes.
For example:
class Eq a => Ord a where
(<),(<=),(>),(>=) :: a -> a -> Bool max, min :: a -> a -> a
9
❚ This means that an instance of Ord must not only provide the functions of Ord’s signature but also of Eq’s signature.
❚ The functions of Eq can then be used to implement the functions of Ord.
❚ Ord is inheriting operations from Eq.
10
❚ Type a cannot appear in a (class) context not mentioned in the instance declaration.
class Eq a => ShowEq a where ifS :: Show a => a -> a -> String foo :: Show b => b -> (String,a)
class (Eq a,Show a) => ShowEq a where
ifS :: a -> a -> String
foo :: Show b => b -> (String,a)
11
Multiple Inheritance
Here the operation (==) from Eq and show form Show have been inherited.
Several contexts can be used (more than two).
class (Eq a,Show a) => ShowEq a where
ifS :: a -> a -> String ifS x y = if x==y then “tie”
else show x
3
12
Derived Instances
Compound types may be instances of classes if constituent types are instances of the same class.
For example:
instance Eq a => Eq [a] where [] == [] = True
(x:xs) == (y:ys) = x==y && (xs==ys) equality of type a
13
❚ So the equality on a list depends on the equality on the elements of the list.
❚ Similar for tuple types.
❚ Also here multiple constraints may appear. For example:
instance (Eq a, Eq b) => Eq (a,b) where
(x1,y1) == (x2,y2) = x1==x2 && y1==y2
14
Data Types as Class Instances
For data types there is a special syntax to declare them as instances of a certain class:
data Tree a = Leaf a
| Node (Tree a) a (Tree a) deriving (Eq,Ord,Show,Read)
Tree is an instance of classes Eq, Ord, Show, and Read.
list of classes the type is an instance of
15
❚ Keyword deriving indicates the following implicit instance declarations.
❚ You can only use the following classes:
Eq, Ord, Show, Enum, Read
as for others the instances cannot be computed automatically.
❚ Ord defines a lexicographic ordering on the data type.
16
❚ Constructors are ordered by order of appearance. For example
(Leaf 30) < (Node (Leaf 1) 1 (Leaf 2)) (Leaf 3) < (Leaf 11)
❚ Class Show allows usage of function show : a -> String
which converts a value into a string.
> show (Node (Leaf 1) 1 (Leaf 2))
“Node (Leaf 1) 1 (Leaf 2)”
17
❚ Read is the opposite of Show and allows one to parse values of the given type from a string.
❚ The function it provides is:
❚ This parser works for all values of Haskell types in Haskell syntax. Note that the string must be completely consumed by the parser.
read :: Read a => String -> a
4
18
> read ”34” + 3 37
> head (read “[1,2]”) + 3 4
> read “34xx” + 3
Program error Prelude. read no parse
Examples for Read
19
Enumeration Types
❚ If all constructors are nullary then this defines an enumeration type and one can declare it to be an instance of class Enum. For example:
data Car = Ford | Jag | Merc | Porsche deriving (Show,Read,Ord,Eq,Enum)
declares an enumeration type
> [Jag .. ] [Jag,Merc,Porsche]
space important here
20
Higher-Order Classes
❚ Classes can not only be defined on types, but also on type constructors.
❚ Type constructors are for example:
function space, lists, tuples, triples etc.
❚ An example is the monad class Monad a.
(->), [], (,), (,,)
21
❚ One can declare an instance of the list monad like this:
❚ This is already done in Prelude.
❚ One can not declare a type alias defined via type N a = … as instance of a class.
instance Monad [] where return t = [t]
xs >>= f = concat (map f xs)
22
Comparison with OOP
❚ In Haskell and OOP classes capture a common set of operations.
❚ In both cases we have instances, inheritance, default implementations and overriding.
❚ In Haskell instances are types that have no mutable state. Correspondingly, it can be computed at compile time which overloaded operation is used.
23
Kinds of Polymorphism
❚ Subtype polymorphism in OOP. Which operation is called depends on dynamic type of object.
❚ Generic (parametric) polymorphism in Haskell combined with overloading in a neat way. Type classes provide smaller “universes” of types.
f :: a -> Bool f :: C a => a -> Bool
possible instances of a:
all types in class C all types
1 FFF FU U U UN N N NC C C CTT TIIIIO T ON O O NA N N ALLL A A L
P P P
PR R R RO O O OG G G GR RA R R AM A A MM M M M M MIIIIN N N NG G G G
Bernhard Reus
Lecture 14 - Lazy Evaluation
1
Lazy Evaluation…
❚ … denotes a certain evaluation strategy that only evaluates an expression when the result is needed.
❚ … allows for infinite data structures.
❚ … supports a more abstract programming style
❚ Most other languages are eager.
2
Lazy Function Application
❚ Recall the boolean operator (&&) that only evaluates its second argument if the first one was True.
❚ One can test whether an argument of a function is evaluated by applying the function to
undefined:
> False && undefined False
> undefined && True Program error: {undefined}
3
❚ Arguments of functions are evaluated by need.
❚ If an argument has to be evaluated, however, then it is evaluated just once.
For example in
(2+3) is evaluated only once and (12^2) never.
f x y = x + x + x
>f (2+3) (12^2) 15
4
Lazy Data Structures
❚ If a structured element like
* a tuple (s,t)
* a list x:xs
* a value of a datatype C(t1,…tn)
is evaluated then only those parts are evaluated that are really needed.
❚ For example, in
the argument list is only evaluated until its head element is known.
> head (1:undefined) 1
5
Infinite Data Structures
❚ Since evaluation is lazy, values of data types can be infinite by recursion.
❚ For example, this recursive definition of a list
defines an infinite list of 1s.
xs :: [Int]
xs = 1:xs
> xs
[1,1,1,1,1,1,1,^C {Interrupted}
2
6
xs 1:xs
=
1:(1:xs)
=
1:(1:(1:xs))
=
[1,1,1,1,1,1,1,1,1,…
= Evaluation of
yields
> xs
7
Evaluation of
take 2 xs take 2 (1:xs)
=
1:(take 1 xs)
=
1:(take 1 (1:xs))
=
1:1:(take 0 xs)
= yields
> take 2 xs
1:1:[] = [1,1]
=
8
Sieve of Eratosthenes
This algorithm, over 2000 years old, computes the list of prime numbers.
Idea:
2 3 4 5 6 7 8 9 10 11 12 13 …
❚ Start with a list of all numbers but 1.
❚ Once a prime number is found cancel out all its multiples.
9
2 3 4 5 6 7 8 9 10 11 12 13 … 2 3 4 5 6 7 8 9 10 11 12 13 …
2 3 4 5 6 7 8 9 10 … 14 15 … 2 3 4 5 6 7 8 9 10 11 12 13 …
2 3 4 5 6 7 8 9 10 … 14 15 …
10
Sieve Algorithm
❚ Start with a list of all numbers but 1.
❚ The head of the list is a prime number.
❚ Cancel out all multiples of the head in the rest list.
❚ Repeat that algorithm for the new rest list.
sieve :: [Int] -> [Int]
sieve (x:xs) =
x:(sieve [y | y <- xs, y `mod` x > 0]) prime :: [Int]
prime = sieve [2..]
11
Evaluation of
prime sieve [2..]
=
2:(sieve [y| y<-[3..], y`mod`2 > 0])
=
2:(sieve (3:[y| y<-[4..], y`mod`2 > 0]))
=
2:3:(sieve [z|z<-[y|y<-[4..],y`mod`2>0]
, z `mod` 3 > 0 ])
= yields
prime
…
=
3
12
❚ prime can be used to check primality but be careful:
❚ Not to be in prime takes an infinite number of tests to check.
❚ Do we really have to test for all numbers in the list?
> elem prime 7 True
> elem prime 6
no reply!
13
elemOrd:: Ord a => [a] -> a -> Bool elemOrd (x:xs) y
| x < y = elemOrd xs y
| x == y = True
| otherwise = False
>elemOrd prime 6 False
No. Use the fact that the list is ordered:
14
Process Networks
❚ In Haskell not only lists but any data structure is implicitly infinite.
❚ But infinite lists are particularly interesting.
❚ They model data streams in networks where input and output are streams. Streams are manipulated by stream processing operations.
15
ai + bi
(0:) feedback
b1,b2,b3,…
a0,a1,a2,…
0,b1,b2,b3…
A Sample Network
result input
16
network as = plus as (0:(network as)) where plus xs ys =
map (\(x,y)-> x+y) (zip xs ys)
>take 5 (network [1..]) [0,1,3,6,10]
This process network can be expressed using infinite lists and recursion. For example:
What does this compute?
bn =
a
0+ a
1+ a
2+ … +
a n-117
Rabbit Reproduction
In an “ideal rabbit world” rabbits reproduce at the age of 2 months and from then on produce a new couple of rabbits every month.
If you start with one newborn couple, how many couples do you have after n months if no rabbits die (it’s an ideal world after all)?
4
18
How many rabbit couples are there ?
0 1 2 3 4
Months couples
+ + +
+
19
❚ Building principle:
❚ These numbers are called Fibonacci numbers.
❚ They occur often in nature (for example the number of florets in sunflowers etc. ).
❚ rabbits(n+1)/rabbits(n) converges to the golden ratio.
old rabbits new rabbits rabbits(n+2) = rabbits(n+1) + rabbits(n)
20
+
tail
feedback
1,1,b2,b3,b4,…
b2,b3,b4,…
Network for Fibonacci
(1:1:) 1,b2,b3,b4,…
output
21
fib = 1:1:(plus fib (tail fib)) where plus xs ys =
map (\(x,y) -> x+y)(zip xs ys)
> take 7 fib [1,1,2,3,5,8,13]
Implementation of the Fibonacci network using infinite lists:
22
Advantages of Infinite Lists
❚ Abstraction: programs can be designed without care about the possible length of the lists.
❚ Stream-based (process-based) programming of networks. Feedback by recursion.
❚ Making use of higher-order functions one can develop a library of network components that can be composed.
23
Exercises
Write a process network that merges two given input streams of numbers that are supposed to be ordered.
Write mergesort as a process network. Can it work for infinite lists?
Define a process network that computes the output stream of Hamming numbers in ascending order. The Hamming numbers are all the numbers which have prime factors in [2, 3, 5].
(1)
(2)
(3)