# 0 PROGRAMMING IN HASKELL Chapter 11 - Interactive Programs, Declaring Types and Classes

Post on 28-Dec-2015

215 views

Embed Size (px)

TRANSCRIPT

<ul><li><p>*PROGRAMMING IN HASKELLChapter 11 - Interactive Programs,Declaring Types and Classes</p></li><li><p>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.</p></li><li><p>*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.</p></li><li><p>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.</p></li><li><p>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 aThe type of actions that return a value of type a.</p></li><li><p>*For example:IO CharIO ()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:</p></li><li><p>Basic Actions*The standard library provides a number of actions, including the following three primitives:getChar :: IO CharThe action getChar reads a character from the keyboard, echoes it to the screen, and returns the character as its result value:</p></li><li><p>*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</p></li><li><p>Sequencing*A sequence of actions can be combined as a single composite action using the keyword do.</p><p>For example:a :: IO (Char,Char)a = do x getChar getChar y getChar return (x,y)</p></li><li><p>Derived Primitives*getLine :: IO StringgetLine = do x getChar if x == '\n' then return [] else do xs getLine return (x:xs)Reading a string from the keyboard:</p></li><li><p>*putStr :: String IO ()putStr [] = return ()putStr (x:xs) = do putChar x putStr xsWriting a string to the screen:Writing a string and moving to a new line:putStrLn :: String IO ()putStrLn xs = do putStr xs putChar '\n'</p></li><li><p>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"</p></li><li><p>*For example:> strlen</p><p>Enter a string: abcdeThe string has 5 charactersEvaluating an action executes its side effects, with the final result value being discarded.Note:</p></li><li><p>Hangman*Consider the following version of hangman:One player secretly types in a word.</p><p>The other player tries to deduce the word, by entering a sequence of guesses. </p><p>For each guess, the computer indicates which letters in the secret word occur in the guess.</p></li><li><p>*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 wordWe adopt a top down approach to implementing hangman in Haskell, starting as follows:</p></li><li><p>*The action sgetLine reads a line of text from the keyboard, echoing each character as a dash:sgetLine :: IO StringsgetLine = do x getCh if x == '\n' then do putChar x return [] else do putChar '-' xs sgetLine return (x:xs)</p></li><li><p>*primitive getCh :: IO CharNote:The action getCh reads a character from the keyboard, without echoing it to the screen.</p><p>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:</p></li><li><p>*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</p></li><li><p>*The function diff indicates which characters in one string occur in a second string:For example:> diff "haskell" "pascal" "-as--ll"diff :: String String Stringdiff xs ys = [if elem x ys then x else '-' | x xs]</p></li><li><p>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: *</p></li><li><p>*Two players take it turn about to remove one or more stars from the end of a single row.</p><p>The winner is the player who removes the last star or stars from the board.Hint:</p><p>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].</p></li><li><p>Type Declarations*In Haskell, a new name for an existing type can be defined using a type declaration.type String = [Char]String is a synonym for the type [Char].</p></li><li><p>*Type declarations can be used to make other types easier to read. For example, givenorigin :: Posorigin = (0,0)</p><p>left :: Pos Posleft (x,y) = (x-1,y)type Pos = (Int,Int)we can define:</p></li><li><p>*Like function definitions, type declarations can also have parameters. For example, giventype Pair a = (a,a)we can define:mult :: Pair Int Intmult (m,n) = m*n</p><p>copy :: a Pair acopy x = (x,x)</p></li><li><p>*Type declarations can be nested:type Pos = (Int,Int)</p><p>type Trans = Pos PosHowever, they cannot be recursive:type Tree = (Int,[Tree])</p></li><li><p>Data Declarations*A completely new type can be defined by specifying its values using a data declaration.data Bool = False | TrueBool is a new type, with two new values False and True.</p></li><li><p>*Note:The two values False and True are called the constructors for the type Bool.</p><p>Type and constructor names must begin with an upper-case letter.</p><p>Data declarations are similar to context free grammars. The former specifies the values of a type, the latter the sentences of a language.</p></li><li><p>*answers :: [Answer]answers = [Yes,No,Unknown]</p><p>flip :: Answer Answerflip Yes = Noflip No = Yesflip Unknown = Unknowndata Answer = Yes | No | Unknownwe can define:Values of new types can be used in the same ways as those of built in types. For example, given </p></li><li><p>*The constructors in a data declaration can also have parameters. For example, givendata Shape = Circle Float | Rect Float Floatsquare :: Float Shapesquare n = Rect n n</p><p>area :: Shape Floatarea (Circle r) = pi * r^2area (Rect x y) = x * ywe can define:</p></li><li><p>*Note:Shape has values of the form Circle r where r is a float, and Rect x y where x and y are floats.</p><p>Circle and Rect can be viewed as functions that construct values of type Shape:Circle :: Float Shape</p><p>Rect :: Float Float Shape</p></li><li><p>*Not surprisingly, data declarations themselves can also have parameters. For example, givendata Maybe a = Nothing | Just asafediv :: Int Int Maybe Intsafediv _ 0 = Nothingsafediv m n = Just (m `div` n)</p><p>safehead :: [a] Maybe asafehead [] = Nothingsafehead xs = Just (head xs)we can define:</p></li><li><p>Recursive Types*In Haskell, new types can be declared in terms of themselves. That is, types can be recursive.data Nat = Zero | Succ NatNat is a new type, with constructors Zero :: Nat and Succ :: Nat Nat.</p></li><li><p>*Note:A value of type Nat is either Zero, or of the form Succ n where n :: Nat. That is, Nat contains the following infinite sequence of values:ZeroSucc ZeroSucc (Succ Zero)</p></li><li><p>*We can think of values of type Nat as natural numbers, where Zero represents 0, and Succ represents the successor function 1+.</p><p>For example, the valueSucc (Succ (Succ Zero))represents the natural number</p></li><li><p>*Using recursion, it is easy to define functions that convert between values of type Nat and Int:nat2int :: Nat Intnat2int Zero = 0nat2int (Succ n) = 1 + nat2int n</p><p>int2nat :: Int Natint2nat 0 = Zeroint2nat (n+1) = Succ (int2nat n)</p></li><li><p>*Two naturals can be added by converting them to integers, adding, and then converting back:However, using recursion the function add can be defined without the need for conversions:add :: Nat Nat Natadd m n = int2nat (nat2int m + nat2int n)add Zero n = nadd (Succ m) n = Succ (add m n) </p></li><li><p>*For example:add (Succ (Succ Zero)) (Succ Zero)</p></li><li><p>Arithmetic Expressions*Consider a simple form of expressions built up from integers using addition and multiplication.</p></li><li><p>*Using recursion, a suitable new type to represent such expressions can be declared by:For example, the expression on the previous slide would be represented as follows:data Expr = Val Int | Add Expr Expr | Mul Expr ExprAdd (Val 1) (Mul (Val 2) (Val 3))</p></li><li><p>*Using recursion, it is now easy to define functions that process expressions. For example:size :: Expr Intsize (Val n) = 1size (Add x y) = size x + size ysize (Mul x y) = size x + size y </p><p>eval :: Expr Inteval (Val n) = neval (Add x y) = eval x + eval yeval (Mul x y) = eval x * eval y</p></li><li><p>*Note:The three constructors have types:Val :: Int ExprAdd :: Expr Expr ExprMul :: Expr Expr ExprMany functions on expressions can be defined by replacing the constructors by other functions using a suitable fold function. For example:eval = fold id (+) (*)</p></li><li><p>Binary Trees*In computing, it is often useful to store data in a two-way branching structure or binary tree.</p></li><li><p>*Using recursion, a suitable new type to represent such binary trees can be declared by:For example, the tree on the previous slide would be represented as follows:data Tree = Leaf Int | Node Tree Int TreeNode (Node (Leaf 1) 3 (Leaf 4)) 5 (Node (Leaf 6) 7 (Leaf 9))</p></li><li><p>*We can now define a function that decides if a given integer occurs in a binary tree:occurs :: Int Tree Booloccurs m (Leaf n) = m==noccurs m (Node l n r) = m==n || occurs m l || occurs m rBut in the worst case, when the integer does not occur, this function traverses the entire tree.</p></li><li><p>*Now consider the function flatten that returns the list of all the integers contained in a tree:flatten :: Tree [Int]flatten (Leaf n) = [n]flatten (Node l n r) = flatten l ++ [n] ++ flatten rA tree is a search tree if it flattens to a list that is ordered. Our example tree is a search tree, as it flattens to the ordered list [1,3,4,5,6,7,9].</p></li><li><p>*Search trees have the important property that when trying to find a value in a tree we can always decide which of the two sub-trees it may occur in:This new definition is more efficient, because it only traverses one path down the tree.occurs m (Leaf n) = m==noccurs m (Node l n r) | m==n = True | mn = occurs m r</p></li><li><p>Exercises*</p><p>*</p></li></ul>