FUNCTIONAL PROGRAMMING 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. Graham Hutton inputs batch program outputs Lecture 9 - Interactive Programs 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. keyboard The Problem Haskell programs are pure mathematical functions: Haskell programs have no side effects. inputs interactive program outputs However, reading from the keyboard and writing to the screen are side effects: screen 2 Interactive programs have side effects. 3 The Solution Interactive programs can be written in Haskell by using types to distinguish pure expressions from impure actions that may involve side effects. For example: IO Char The type of actions that return a character. IO a The type of actions that return a value of type a. Note: IO () The type of purely side effecting actions that return no result value. () is the type of tuples with no components. 4 5
Primitive Actions The standard library provides a number of actions, including the following three primitives: The action putchar c writes the character c to the screen, and returns no result value: putchar :: Char IO () The action getchar reads a character from the keyboard, echoes it to the screen, and returns the character as its result value: The action return v simply returns the value v, without performing any interaction: getchar :: IO Char return :: a IO a 6 7 Sequencing Actions Note - in a sequence of actions: A sequence of actions can be combined as a single composite action using the keyword do. For example: gettwo :: IO (Char,Char) gettwo do x getchar y getchar return (x,y) 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. 8 9 Other Library Actions Writing a string to the screen: Reading a string from the keyboard: getline :: IO String getline do x getchar if x '\n' then return [] else do xs getline return (x:xs) putstr :: String IO () putstr [] return () putstr (x:xs) do putchar x putstr xs Writing a string and moving to a new line: putstrln :: String IO () putstrln xs do putstr xs putchar '\n' 0 2
Example For 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" Note: > strlen Enter a string: hello there The string has characters Evaluating an action executes its side effects, with the final result value being discarded. 2 3 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. The game ends when the guess is correct. We adopt a top down approach to implementing hangman in Haskell, starting as follows: hangman :: IO () hangman do putstrln "Think of a word: " word sgetline putstrln "Try to guess it:" guess word 4 5 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) 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: primitive getch :: IO Char 6 7 3
The function guess is the main loop, which requests and processes guesses until the game ends. The function diff indicates which characters in one string occur in a second string: 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 8 diff :: String String String diff xs ys [if elem x ys then x else '-' x xs] For example: > diff "haskell" "pascal" "-as--ll" 9 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 Exercise Implement the game of nim in Haskell, where the rules of the game are as follows: > hangman Think of a word: ------- Try to guess it: > pascal -as--ll > haskell You got it!! 20 The board comprises five rows of stars: : * * * * * 2: * * * * 3: * * * 4: * * 5: * 2 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,]. 22 4
)8&7,2$/352*5$00,* 7\SH'HFODUDWLRQV,QÃ+DVNHOOÃDÃQHZÃQDPHÃIRUÃDQÃH[LVWLQJÃW\SHÃFDQÃEH GHILQHGÃXVLQJÃDÃW\SHÃGHFODUDWLRQ W\SHÃ6WULQJÃ Ã>&KDU@ *UDKDP+XWWRQ /HFWXUH'HILQLQJ7\SHV 6WULQJÃLVÃDÃV\QRQ\PÃIRUÃWKHÃW\SHÃ>&KDU@ 7\SHÃGHFODUDWLRQVÃFDQÃEHÃXVHGÃWRÃPDNHÃRWKHUÃW\SHV HDVLHUÃWRÃUHDGÃÃ)RUÃH[DPSOHÃJLYHQ /LNHÃIXQFWLRQÃGHILQLWLRQVÃW\SHÃGHFODUDWLRQVÃFDQÃDOVR KDYHÃSDUDPHWHUVÃÃ)RUÃH[DPSOHÃJLYHQ W\SHÃ3RVÃ Ã,QW,QW W\SHÃ3DLUÃDÃ ÃDD ZHÃFDQÃGHILQH ZHÃFDQÃGHILQH RULJLQÃÃÃÃÃ3RV RULJLQÃÃÃÃÃ Ã OHIWÃÃÃÃÃÃÃ3RVÃ Ã3RV OHIWÃ[\Ã Ã[\ ELWVÃÃÃ3DLUÃ,QW ELWVÃÃÃ Ã FRS\ÃÃÃDÃ Ã3DLUÃD FRS\Ã[Ã Ã[[ 7\SHÃGHFODUDWLRQVÃFDQÃEHÃQHVWHG 'DWD'HFODUDWLRQV W\SHÃ3RVÃÃÃ Ã,QW,QW W\SHÃ7UDQVÃ Ã3RVÃ Ã3RV $ÃQHZÃW\SHÃFDQÃEHÃGHILQHGÃE\ÃVSHFLI\LQJÃLWVÃVHWÃRI YDOXHVÃXVLQJÃDÃGDWDÃGHFODUDWLRQ +RZHYHUÃWKH\ÃFDQQRWÃEHÃUHFXUVLYH GDWDÃ%RROÃ Ã)DOVHÃ_Ã7UXH W\SHÃ7UHHÃ Ã,QW>7UHH@ %RROÃLVÃDÃQHZÃW\SHÃZLWKÃWZR QHZÃYDOXHVÃ)DOVHÃDQGÃ7UXH
RWH 9DOXHVÃRIÃQHZÃW\SHVÃFDQÃEHÃXVHGÃLQÃWKHÃVDPHÃZD\V DVÃWKRVHÃRIÃEXLOWÃLQÃW\SHVÃÃ)RUÃH[DPSOHÃJLYHQ 7KHÃWZRÃYDOXHVÃ)DOVHÃDQGÃ7UXHÃDUHÃFDOOHGÃWKH FRQVWUXFWRUVÃIRUÃWKHÃW\SHÃ%RRO GDWDÃ$QVZHUÃ Ã<HVÃ_ÃRÃ_Ã8QNQRZQ 7\SHÃDQGÃFRQVWUXFWRUÃQDPHVÃPXVWÃEHJLQÃZLWK DQÃXSSHUFDVHÃOHWWHU 'DWDÃGHFODUDWLRQVÃDUHÃVLPLODUÃWRÃFRQWH[WÃIUHH JUDPPDUVÃÃ7KHÃIRUPHUÃVSHFLILHVÃWKHÃYDOXHVÃRI DÃW\SHÃWKHÃODWWHUÃWKHÃVHQWHQFHVÃRIÃDÃODQJXDJH ZHÃFDQÃGHILQH DQVZHUVÃÃÃÃÃÃ>$QVZHU@ DQVZHUVÃÃÃÃÃÃ Ã><HVR8QNQRZQ@ IOLSÃÃÃÃÃÃÃÃÃ$QVZHUÃ Ã$QVZHU IOLSÃ<HVÃÃÃÃÃ ÃR IOLSÃRÃÃÃÃÃÃ Ã<HV IOLSÃ8QNQRZQÃ Ã8QNQRZQ 7KHÃFRQVWUXFWRUVÃLQÃDÃGDWDÃGHFODUDWLRQÃFDQÃDOVRÃKDYH SDUDPHWHUVÃÃ)RUÃH[DPSOHÃJLYHQ RWH GDWDÃ6KDSHÃ Ã&LUFOHÃ)ORDW ÃÃÃÃÃÃÃÃÃÃÃ_Ã5HFWÃ)ORDWÃ)ORDW 6KDSHÃKDVÃYDOXHVÃRIÃWKHÃIRUPÃ&LUFOHÃUÃZKHUHÃUÃLV DÃIORDWÃDQGÃ5HFWÃ[Ã\ÃZKHUHÃ[ÃDQGÃ\ÃDUHÃIORDWV ZHÃFDQÃGHILQH VTXDUHÃÃÃÃÃÃÃÃÃÃ6KDSH VTXDUHÃÃÃÃÃÃÃÃÃÃ Ã5HFWÃÃ DUHDÃÃÃÃÃÃÃÃÃÃÃÃ6KDSHÃ Ã)ORDW DUHDÃ&LUFOHÃUÃ ÃSLÃÃUA DUHDÃ5HFWÃ[Ã\Ã Ã[ÃÃ\ &LUFOHÃDQGÃ5HFWÃFDQÃEHÃYLHZHGÃDVÃIXQFWLRQVÃWKDW VLPSO\ÃFRQVWUXFWÃYDOXHVÃRIÃW\SHÃ6KDSH &LUFOHÃÃ)ORDWÃ Ã6KDSH 5HFWÃÃÃÃ)ORDWÃ Ã)ORDWÃ Ã6KDSH RWÃVXUSULVLQJO\ÃGDWDÃGHFODUDWLRQVÃWKHPVHOYHVÃFDQ DOVRÃKDYHÃSDUDPHWHUVÃÃ)RUÃH[DPSOHÃJLYHQ 5HFXUVLYH7\SHV GDWDÃ0D\EHÃDÃ ÃRWKLQJÃ_Ã-XVWÃD,QÃ+DVNHOOÃQHZÃW\SHVÃFDQÃEHÃGHILQHGÃLQÃWHUPVÃRI WKHPVHOYHVÃÃ7KDWÃLVÃW\SHVÃFDQÃEHÃUHFXUVLYH ZHÃFDQÃGHILQH ]HURÃÃ0D\EHÃ,QW ]HURÃÃ Ã-XVWÃ GDWDÃDWÃ ÃHURÃ_Ã6XFFÃDW DSSÃÃÃDÃ ÃEÃ Ã0D\EHÃDÃ Ã0D\EHÃE DSSÃIÃRWKLQJÃÃ ÃRWKLQJ DSSÃIÃ-XVWÃ[Ã Ã-XVWÃIÃ[ DWÃLVÃDÃQHZÃW\SHÃZLWKÃFRQVWUXFWRUV HURÃÃDWÃDQGÃ6XFFÃÃDWÃ ÃDW
RWH $ÃYDOXHÃRIÃW\SHÃDWÃLVÃHLWKHUÃHURÃRUÃRIÃWKHÃIRUP 6XFFÃQÃZKHUHÃQÃÃDWÃÃ7KDWÃLVÃDWÃFRQWDLQVÃWKH IROORZLQJÃLQILQLWHÃVHTXHQFHÃRIÃYDOXHV :HÃFDQÃWKLQNÃRIÃYDOXHVÃRIÃW\SHÃDWÃDVÃQDWXUDO QXPEHUVÃZKHUHÃHURÃUHSUHVHQWVÃÃDQGÃ6XFF UHSUHVHQWVÃWKHÃVXFFHVVRUÃIXQFWLRQÃÃ )RUÃH[DPSOHÃWKHÃYDOXH HUR 6XFFÃHUR 6XFFÃ6XFFÃHUR 6XFFÃ6XFFÃ6XFFÃHUR UHSUHVHQWVÃWKHÃQDWXUDOÃQXPEHU ÃÃÃÃÃÃ 8VLQJÃUHFXUVLRQÃLWÃLVÃHDV\ÃWRÃGHILQHÃIXQFWLRQVÃWKDW FRQYHUWÃEHWZHHQÃYDOXHVÃRIÃW\SHÃDWÃDQGÃ,QW 7ZRÃQDWXUDOVÃFDQÃEHÃDGGHGÃE\ÃFRQYHUWLQJÃWKHPÃWR LQWHJHUVÃDGGLQJÃDQGÃWKHQÃFRQYHUWLQJÃEDFN QDWLQWÃÃÃÃÃÃÃÃÃÃDWÃ Ã,QW QDWLQWÃHURÃÃÃÃÃ Ã QDWLQWÃ6XFFÃQÃ ÃÃÃQDWLQWÃQ LQWQDWÃÃÃÃÃÃÃÃÃÃ,QWÃ ÃDW LQWQDWÃÃÃÃÃÃÃÃÃ ÃHUR LQWQDWÃQÃÃÃÃ Ã6XFFÃLQWQDWÃQ DGGÃÃÃÃÃDWÃ ÃDWÃ ÃDW DGGÃPÃQÃ ÃLQWQDWÃQDWLQWÃPÃÃQDWLQWÃQ +RZHYHUÃXVLQJÃUHFXUVLRQÃWKHÃIXQFWLRQÃDGGÃFDQÃEH GHILQHGÃZLWKRXWÃWKHÃQHHGÃIRUÃFRQYHUVLRQV DGGÃHURÃÃÃÃÃQÃ ÃQ DGGÃ6XFFÃPÃQÃ Ã6XFFÃDGGÃPÃQÃ )RUÃH[DPSOH DGGÃ6XFFÃ6XFFÃHURÃ6XFFÃHUR 6XFFÃDGGÃ6XFFÃHURÃ6XFFÃHUR $ULWKPHWLF([SUHVVLRQV &RQVLGHUÃDÃVLPSOHÃIRUPÃRIÃH[SUHVVLRQVÃEXLOWÃXSÃIURP LQWHJHUVÃXVLQJÃDGGLWLRQÃDQGÃPXOWLSOLFDWLRQ 6XFFÃ6XFFÃDGGÃHURÃ6XFFÃHUR 6XFFÃ6XFFÃ6XFFÃHUR RWH 7KHÃUHFXUVLYHÃGHILQLWLRQÃIRUÃDGGÃFRUUHVSRQGVÃWR WKHÃODZVÃQÃ ÃQÃDQGÃPQÃ ÃPQ
8VLQJÃUHFXUVLRQÃDÃVXLWDEOHÃQHZÃW\SHÃWRÃUHSUHVHQW VXFKÃH[SUHVVLRQVÃFDQÃEHÃGHILQHGÃE\ 8VLQJÃUHFXUVLRQÃLWÃLVÃQRZÃHDV\ÃWRÃGHILQHÃIXQFWLRQV WKDWÃSURFHVVÃH[SUHVVLRQVÃÃ)RUÃH[DPSOH GDWDÃ([SUÃ Ã9DOÃ,QW ÃÃÃÃÃÃÃÃÃÃ_Ã$GGÃ([SUÃ([SU ÃÃÃÃÃÃÃÃÃÃ_Ã0XOÃ([SUÃ([SU )RUÃH[DPSOHÃWKHÃH[SUHVVLRQÃRQÃWKHÃSUHYLRXVÃVOLGH ZRXOGÃEHÃUHSUHVHQWHGÃDVÃIROORZV $GGÃ9DOÃÃ0XOÃ9DOÃÃ9DOÃ VL]HÃÃÃÃÃÃÃÃÃÃÃ([SUÃ Ã,QW VL]HÃ9DOÃQÃÃÃ Ã VL]HÃ$GGÃ[Ã\Ã ÃVL]HÃ[ÃÃVL]HÃ\ VL]HÃ0XOÃ[Ã\Ã ÃVL]HÃ[ÃÃVL]HÃ\ HYDOÃÃÃÃÃÃÃÃÃÃÃ([SUÃ Ã,QW HYDOÃ9DOÃQÃÃÃ ÃQ HYDOÃ$GGÃ[Ã\Ã ÃHYDOÃ[ÃÃHYDOÃ\ HYDOÃ0XOÃ[Ã\Ã ÃHYDOÃ[ÃÃHYDOÃ\ RWH 7KHÃWKUHHÃFRQVWUXFWRUVÃKDYHÃW\SHV 9DOÃÃ,QWÃ Ã([SU $GGÃÃ([SUÃ Ã([SUÃ Ã([SU 0XOÃÃ([SUÃ Ã([SUÃ Ã([SU %LQDU\7UHHV,QÃFRPSXWLQJÃLWÃLVÃRIWHQÃXVHIXOÃWRÃVWRUHÃGDWDÃLQÃD WZRZD\ÃEUDQFKLQJÃVWUXFWXUHÃRUÃELQDU\ÃWUHH 0DQ\ÃIXQFWLRQVÃRQÃH[SUHVVLRQVÃFDQÃEHÃGHILQHG E\ÃUHSODFLQJÃWKHÃFRQVWUXFWRUVÃE\ÃRWKHUÃIXQFWLRQV XVLQJÃDÃVXLWDEOHÃIROGÃIXQFWLRQÃÃ)RUÃH[DPSOH HYDOÃ ÃIROGÃLGÃÃ 8VLQJÃUHFXUVLRQÃDÃVXLWDEOHÃQHZÃW\SHÃWRÃUHSUHVHQW VXFKÃELQDU\ÃWUHHVÃFDQÃEHÃGHILQHGÃE\ :HÃFDQÃGHILQHÃDÃIXQFWLRQÃILQGÃWKDWÃGHFLGHVÃLIÃDÃJLYHQ LQWHJHUÃRFFXUVÃLQÃDÃELQDU\ÃWUHH GDWDÃ7UHHÃ Ã/HDIÃ,QW ÃÃÃÃÃÃÃÃÃÃ_ÃRGHÃ7UHHÃ,QWÃ7UHH )RUÃH[DPSOHÃWKHÃWUHHÃRQÃWKHÃSUHYLRXVÃVOLGHÃZRXOG EHÃUHSUHVHQWHGÃDVÃIROORZV ILQGÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃ,QWÃ Ã7UHHÃ Ã%RRO ILQGÃ[Ã/HDIÃQÃÃÃÃÃ Ã[ Q ILQGÃ[ÃRGHÃOÃQÃUÃ Ã[ Q ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃ ÃILQGÃ[ÃO ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃ ÃILQGÃ[ÃU RGHÃRGHÃ/HDIÃÃÃ/HDIÃ ÃÃÃÃÃ ÃÃÃÃÃRGHÃ/HDIÃÃÃ/HDIÃ +RZHYHUÃWKLVÃIXQFWLRQÃVLPSO\ÃWUDYHUVHVÃWKHÃHQWLUH WUHHÃDQGÃKHQFHÃIRUÃRXUÃH[DPSOHÃWUHHÃPD\ÃUHTXLUH XSÃWRÃVHYHQÃFRPSDULVRQVÃWRÃSURGXFHÃDÃUHVXOW
RZÃFRQVLGHUÃWKHÃIXQFWLRQÃIODWWHQÃWKDWÃUHWXUQVÃWKH OLVWÃRIÃDOOÃWKHÃLQWHJHUVÃFRQWDLQHGÃLQÃDÃWUHH IODWWHQÃÃÃÃÃÃÃÃÃÃÃÃÃÃ7UHHÃ Ã>,QW@ IODWWHQÃ/HDIÃQÃÃÃÃÃ Ã>Q@ IODWWHQÃRGHÃOÃQÃUÃ ÃIODWWHQÃO ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃ>Q@ ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃIODWWHQÃU $ÃWUHHÃLVÃDÃVHDUFKÃWUHHÃLIÃLWÃIODWWHQVÃWRÃDÃOLVWÃWKDWÃLV RUGHUHGÃÃ2XUÃH[DPSOHÃWUHHÃLVÃDÃVHDUFKÃWUHHÃDVÃLW IODWWHQVÃWRÃWKHÃRUGHUHGÃOLVWÃ>@ 6HDUFKÃWUHHVÃKDYHÃWKHÃLPSRUWDQWÃSURSHUW\ÃWKDWÃZKHQ WU\LQJÃWRÃILQGÃDÃYDOXHÃLQÃDÃWUHHÃZHÃFDQÃDOZD\VÃGHFLGH ZKLFKÃRIÃWKHÃWZRÃVXEWUHHVÃLWÃPD\ÃRFFXUÃLQ ILQGÃ[Ã/HDIÃQÃÃÃÃÃÃÃÃÃÃÃÃ Ã[ Q ILQGÃ[ÃRGHÃOÃQÃUÃ_Ã[ QÃ Ã7UXH ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃ_Ã[QÃÃ ÃILQGÃ[ÃO ÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃÃ_Ã[!QÃÃ ÃILQGÃ[ÃU )RUÃH[DPSOHÃWU\LQJÃWRÃILQGÃDQ\ÃYDOXHÃLQÃRXUÃVHDUFK WUHHÃRQO\ÃWDNHVÃDWÃPRVWÃWKUHHÃFRPSDULVRQV ([HUFLVHV 8VLQJÃUHFXUVLRQÃDQGÃWKHÃIXQFWLRQÃDGGÃGHILQHÃD IXQFWLRQÃWKDWÃPXOWLSOLHVÃWZRÃQDWXUDOÃQXPEHUV 'HILQHÃDÃVXLWDEOHÃIXQFWLRQÃIROGÃIRUÃH[SUHVVLRQV DQGÃJLYHÃDÃIHZÃH[DPSOHVÃRIÃLWVÃXVH $ÃELQDU\ÃWUHHÃLVÃFRPSOHWHÃLIÃWKHÃWZRÃVXEWUHHVÃRI HYHU\ÃQRGHÃDUHÃRIÃHTXDOÃVL]HÃÃ'HILQHÃDÃIXQFWLRQ WKDWÃGHFLGHVÃLIÃDÃELQDU\ÃWUHHÃLVÃFRPSOHWH
FUNCTIONAL PROGRAMMING The Hugs Graphics Library GraphicsUtils.hs works within Hugs Written by Alastair Reid, University of Utah Library compatible with Win32 and X Bernhard Reus Lecture - Basic Graphics Graphics as Input/Output Embedded in Haskell IO type A First Example Example on Screen Open a window, print "Hello World" in it and close the window as soon as the user releases a key. helloworld :: IO () helloworld rungraphics ( do w <- openwindow "Hello World Window" (300,300) drawinwindow w (text (00,00) "Hello") drawinwindow w (text (00,200) "World") getkey w closewindow w ) 2 3 As graphics are IO one can use the do syntax for sequences of Graphic actions drawinwindow :: Window -> Graphic -> IO () rungraphics :: IO () -> IO () draws a graphic object in a given window prepares Hugs for graphics, runs argument and cleans up. text :: Point -> String -> Graphic openwindow :: Title -> Size -> IO Window opens a window with given title and size and returns it 4 produces a graphic that consists of the given string printed at given coordinates. Point specifies top-left corner 5
Size and Point are types defined as follows Graphic Library Overview type Size (Int,Int) type Point (Int,Int) getkey :: Window -> IO Char width and height x and y-coordinates waits for user to press and release a key and returns corresponding character closewindow :: Window -> IO () closes given window 6 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). 7 Overview (cont d) Windows 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. openwindow :: Title -> Size -> IO Window closewindow :: Window -> IO () clearwindow :: Window -> IO () getgraphic :: Window -> IO Graphic setgraphic :: Window -> Graphic -> IO () drawinwindow :: Window -> Graphic -> IO () 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. 8 9 Windows (cont d) Atomic Graphics getwindowrect :: Window -> IO (Point,Point) text :: Point -> String -> Graphic gets window parameters: location and size as points. Useful when the window has been resized or moved. draws a given string at a given point polygon :: [Point] -> Graphic getwindowevent :: Window -> IO Event gets next window event draws a filled polygon through points in list line :: Point -> Point -> Graphic draws a line between two points 0 2
Atomic Graphics arc :: Point -> Point -> Angle -> Angle -> Graphic draws an unfilled elliptical arc in rectangle defined by two points with two angles (in degrees) as shown beneath: angle2 ellipse :: Point -> Point -> Graphic angle draws a filled ellipse inside given rectangle Graphics & Modifiers overgraphic :: Graphic -> Graphic -> Graphic puts first graphic on top of second withcolor :: Color -> Graphic -> Graphic produces that graphic in given colour withtextalignment :: Alignment -> Graphic -> Graphic produces text in graphic in given alignment 2 3 Modifier Types type Color Black Blue Green Cyan Red Magenta Yellow White Colour names type Alignment (HAlign,VAlign) type HAlign Left Center Right type VAlign Top Baseline Bottom Alignment names. Default (Left,Top) Example hw :: IO () hw rungraphics ( do w <- openwindow "Hello World Window" (300,300) drawinwindow w (text (00,00) "Hello") drawinwindow w (withcolor Blue (text (00,200) "World")) drawinwindow w (withcolor Magenta (polygon [(00,60),(70,50),(00,20)])) getkey w closewindow w ) 4 5 Example Comments Hello World with World in blue Magenta polygon with three nodes triangle 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 6 7 3
Close the window by using the window menu or typing <ALT><F4>. show also works for events and transforms them into strings. Event types 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 We can mix graphic actions with our old IO actions as they are both of type IO a. Boolean parameters: Key : is key down? (or released) Button: is left (or right) mouse button pressed? is button down? (or released) 8 9 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. 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. 20 2 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). 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 [(50,50),(50,70), (70,70),(70,50)] )) drawinwindow w (withcolor Yellow (text (50,50) [c])) loop w 22 23 4
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. Exercises () (2) Write a program that opens a window (of appropriate size) and draws a red square of length 00 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. 24 25 (3) 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 4 pixels. 26 5
FUNCTIONAL PROGRAMMING 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. Bernhard Reus Lecture 2 - Modules!Interfaces state what is exported and what is imported. Advantages of Modules!separate development!separate compilation!re-usability (libraries)!reduce complexity 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. For example, consider a file Gui.hs: module Gui where type Point (Int,Int) movepoint:: Point -> Int -> Point movepoint (x,y) z (x+z,y+z) After loading Gui.hs Hugs will change its prompt indicating that evaluation now is w.r.t. the definitions in module Gui. Gui> Import The import clause 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 p p2) drawshape w (Circle n)
Import Caveats Export Controls!Obey layout rule with respect to import keyword.!by default a module exports all the definitions it contains but not the imported ones.!avoid cyclic imports.!this can be changed by explicitly providing an export list:!make sure that the.hs file containing your module has the right name (that is the same name). module Gui (Shape (..),drawshape) where import GraphicsUtils data Shape drawshape ::!The export list contains the names of the exported objects and types. The list is written in parentheses and names are separated by commas.!therefore, the following two definitions are equivalent: module Gui (module Gui) where! Shape (..) indicates that the type Shape with its constructors is exported. module Gui where!other example:!to export a whole module write: module Gui (module GraphicsUtils) where module Gui (module Gui, module Prelude) where Name Clashes Import Controls!If files are imported then there is always the danger of name clashes. For example,!like export lists one can also define import lists explicitly. For example: module A where import B f x x + 3 module B where f xs foo ++ xs g map f module A where import B (g) f x x + 3 module B where f xs foo ++ xs g map f!to avoid name clashes one can control the import list:!only the names in the list (in parentheses, separated by commas) are imported.!in the definition of g, f still refers to the f of B. 2
Shadowing!Hiding names that can be used for other definitions is also called shadowing.!keyword hiding allows one to shadow several names and import all the rest: module A where import B hiding (f) f x x + 3!Again, the list of names can contain types and names are separated by commas. 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 Qualified Import!Use keyword qualified to make imported names qualified. For example: module A where import qualified B f x x +3 g map B.f!Qualified import can be combined with all the other features of import lists.!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: module A (B.f, C.f) where import qualified B(f) import qualified C(f)!Name clash if one imports from A as B.f and C.f are both referenced outside A as A.f (not A.B.f and A.C.f).!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. 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? 3
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])!note that the constructor S for type Set is 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. How to use Modules!Modules should implement one specific task only and completely. Exercises () Put your solutions of previous exercise sheets in modules.!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). (2) Complete the module Set giving a definition for elimdoubles and 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. (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 4
FUNCTIONAL PROGRAMMING 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. Bernhard Reus Lecture 3 - Classes 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. 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. 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. 2 3 How to Define a Class How to Define an Instance class <Name> <TypeVar> where <signature involving <TypeVar>> 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 Eq a where () :: a -> a -> Bool 4 instance <ClassName> <Type> where <definition of functions in the signature of <ClassName>> The definitions of the functions refer now to the specified type that replaces the type variable in the class definition. Example: instance Eq Bool where True True True True False False False True False False False True 5
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.). Default Implementations class Eq a where (), (/) :: a -> a -> Bool x. / y not (x y) Classes can contain default implementations. Classes can depend on other classes and instances can depend on other instances. Default implementations can be overridden by instance definitions. 6 7 Derived Classes Class definitions may depend on other classes. For example: class Eq a > Ord a where (<),(<),(>),(>) :: a -> a -> Bool 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. max, min :: a -> a -> a 8 9 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) Multiple Inheritance class (Eq a,show a) > ShowEq a where ifs :: a -> a -> String ifs x y if xy then tie else show x Here the operation () from Eq and show form Show have been inherited. Several contexts can be used (more than two). 0 2
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) xy && (xsys) 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 (x,y) (x2,y2) xx2 && yy2 equality of type a 2 3 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) list of classes the type is an instance of Tree is an instance of classes Eq, Ord, Show, and Read. 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. 4 5 Constructors are ordered by order of appearance. For example (Leaf 30) < (Node (Leaf ) (Leaf 2)) (Leaf 3) < (Leaf ) Class Show allows usage of function show : a -> String which converts a value into a string. > show (Node (Leaf ) (Leaf 2)) Node (Leaf ) (Leaf 2) Read is the opposite of Show and allows one to parse values of the given type from a string. The function it provides is: read :: Read a > String -> a This parser works for all values of Haskell types in Haskell syntax. Note that the string must be completely consumed by the parser. 6 7 3
Examples for Read > read 34 + 3 37 > head (read [,2] ) + 3 4 > read 34xx + 3 Program error Prelude. read no parse 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 space important here > [Jag.. ] [Jag,Merc,Porsche] 8 9 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. One can declare an instance of the list monad like this: instance Monad [] where return t [t] xs >> f concat (map f xs) This is already done in Prelude. One can not declare a type alias defined via type N a as instance of a class. 20 2 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. 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. In Haskell instances are types that have no mutable state. Correspondingly, it can be computed at compile time which overloaded operation is used. f :: a -> Bool possible instances of a: all types 22 f :: C a > a -> Bool all types in class C 23 4
FUNCTIONAL PROGRAMMING Lazy Evaluation denotes a certain evaluation strategy that only evaluates an expression when the result is needed. allows for infinite data structures. Bernhard Reus supports a more abstract programming style Lecture 4 - Lazy Evaluation Most other languages are eager. 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} Arguments of functions are evaluated by need. If an argument has to be evaluated, however, then it is evaluated just once. For example in f x y x + x + x >f (2+3) (2^2) 5 (2+3) is evaluated only once and (2^2) never. 2 3 Lazy Data Structures If a structured element like * a tuple (s,t) * a list x:xs * a value of a datatype C(t, tn) is evaluated then only those parts are evaluated that are really needed. For example, in > head (:undefined) the argument list is only evaluated until its head element is known. Infinite Data Structures Since evaluation is lazy, values of data types can be infinite by recursion. For example, this recursive definition of a list xs :: [Int] xs :xs defines an infinite list of s. > xs [,,,,,,,^C {Interrupted} 4 5
Evaluation of > xs yields xs :xs :(:xs) :(:(:xs)) [,,,,,,,,, 6 Evaluation of > take 2 xs yields take 2 xs take 2 (:xs) :(take xs) :(take (:xs)) ::(take 0 xs) ::[] [,] 7 Sieve of Eratosthenes This algorithm, over 2000 years old, computes the list of prime numbers. 2 3 4 5 6 7 8 9 0 2 3 2 3 4 5 6 7 8 9 0 2 3 Idea: Start with a list of all numbers but. 2 3 4 5 6 7 8 9 0 2 3 Once a prime number is found cancel out all its multiples. 2 3 4 5 6 7 8 9 0 4 5 2 3 4 5 6 7 8 9 0 2 3 2 3 4 5 6 7 8 9 0 4 5 8 9 Sieve Algorithm Start with a list of all numbers but. 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..] 0 Evaluation of yields 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 ]) prime 2
prime can be used to check primality but be careful: > elem prime 7 True > elem prime 6 no reply! 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? No. Use the fact that the list is ordered: elemord:: Ord a > [a] -> a -> Bool elemord (x:xs) y x < y elemord xs y x y True otherwise False >elemord prime 6 False 2 3 Process Networks In Haskell not only lists but any data structure is implicitly infinite. But infinite lists are particularly interesting. input a 0,a,a 2, A Sample Network a i + b i b,b 2,b 3, They model data streams in networks where input and output are streams. Streams are manipulated by stream processing operations. 4 0,b,b 2,b 3 (0:) feedback result 5 This process network can be expressed using infinite lists and recursion. For example: network as plus as (0:(network as)) where plus xs ys map (\(x,y)-> x+y) (zip xs ys) What does this compute? >take 5 (network [..]) [0,,3,6,0] 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)? b n a 0 + a + a 2 + + a n- 6 7 3
How many rabbit couples are there? Months couples 0 Building principle: rabbits(n+2) rabbits(n+) + rabbits(n) old rabbits new rabbits 2 3 4 + + + + 8 These numbers are called Fibonacci numbers. They occur often in nature (for example the number of florets in sunflowers etc. ). rabbits(n+)/rabbits(n) converges to the golden ratio. 9 Network for Fibonacci,b 2,b 3,b 4, feedback + b 2,b 3,b 4, Implementation of the Fibonacci network using infinite lists: fib ::(plus fib (tail fib)) where plus xs ys map (\(x,y) -> x+y)(zip xs ys) tail,,b 2,b 3,b 4, (::) > take 7 fib [,,2,3,5,8,3] output 20 2 Advantages of Infinite Lists Exercises Abstraction: programs can be designed without care about the possible length of the lists. () Write a process network that merges two given input streams of numbers that are supposed to be ordered. Stream-based (process-based) programming of networks. Feedback by recursion. (2) Write mergesort as a process network. Can it work for infinite lists? Making use of higher-order functions one can develop a library of network components that can be composed. 22 (3) 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]. 23 4