----------------------------------------------------------------------- -- Haskell: The Craft of Functional Programming -- Simon Thompson -- (c) Addison-Wesley, 1999. -- Chapter 18 ----------------------------------------------------------------------- -- Programming with actions -- ^^^^^^^^^^^^^^^^^^^^^^^^ module Chapter18 where import Prelude hiding (lookup) import IO -- for isEOF (see note below, aslo) isEOF = hugsIsEOF -- this should be commented out in later -- versions; it is here because Hugs 1.4 -- doesn't support isEOF -- The basics of input/output -- ^^^^^^^^^^^^^^^^^^^^^^^^^^ -- Reading input is done by getLine and getChar: see Prelude for details. -- getLine :: IO String -- getChar :: IO Char -- Text strings are written using -- -- putStr :: String -> IO () -- putStrLn :: String -> IO () -- A hello, world program helloWorld :: IO () helloWorld = putStr "Hello, World!" -- Writing values in general -- print :: Show a => a -> IO () -- The do notation: a series of sequencing examples. -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- Put a string and newline. -- putStrLn :: String -> IO () -- putStrLn str = do putStr str -- putStr "\n" -- Put four times. put4times :: String -> IO () put4times str = do putStrLn str putStrLn str putStrLn str putStrLn str -- Put n times putNtimes :: Int -> String -> IO () putNtimes n str = if n <= 1 then putStrLn str else do putStrLn str putNtimes (n-1) str -- Read two lines, then write a message. read2lines :: IO () read2lines = do getLine getLine putStrLn "Two lines read." -- Read then write. getNput :: IO () getNput = do line <- getLine putStrLn line -- Read, process then write. reverse2lines :: IO () reverse2lines = do line1 <- getLine line2 <- getLine putStrLn (reverse line2) putStrLn (reverse line1) -- Last example redefined to use a local definition. reverse2lines' :: IO () reverse2lines' = do line1 <- getLine line2 <- getLine let rev1 = reverse line1 let rev2 = reverse line2 putStrLn rev2 putStrLn rev1 -- Reading an Int. getInt :: IO Int getInt = do line <- getLine return (read line :: Int) -- Iteration and recursion -- ^^^^^^^^^^^^^^^^^^^^^^^ -- A while loop. while :: IO Bool -> IO () -> IO () while test action = do res <- test if res then do action while test action else return () -- Copying input to output. copyInputToOutput :: IO () copyInputToOutput = while (do res <- isEOF return (not res)) (do line <- getLine putStrLn line) -- An important example: refer to the text to see why it fails to work as -- required. (The incorrect version is primed.) goUntilEmpty' :: IO () goUntilEmpty' = do line <- getLine while (return (line /= [])) (do putStrLn line line <- getLine return ()) -- The correct program: the key is to think recursively. goUntilEmpty :: IO () goUntilEmpty = do line <- getLine if (line == []) then return () else (do putStrLn line goUntilEmpty) -- Adding a sequence of integers sumInts :: IO Int sumInts = do n <- getInt if n==0 then return 0 else (do m <- sumInts return (n+m)) -- Addiing a sequence of integers, courteously. sumInteract :: IO () sumInteract = do putStrLn "Enter integers one per line" putStrLn "These will be summed until zero is entered" sum <- sumInts putStr "The sum was " print sum -- The calculator -- ^^^^^^^^^^^^^^ -- This is available separately. -- Input and output as lazy lists -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- Reverse all the lines in the input. listIOprog :: String -> String listIOprog = unlines . map reverse . lines -- Monads for Functional Programming -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- The definition of the Monad class -- class Monad m where -- (>>=) :: m a -> (a -> m b) -> m b -- return :: a -> m a -- fail :: String -> m a -- Kelisli composition for monadic functions. -- (>@>) :: Monad m => (a -> m b) -> -- (b -> m c) -> -- (a -> m c) -- f >@> g = \ x -> (f x) >>= g -- Some examples of monads -- ^^^^^^^^^^^^^^^^^^^^^^^ -- Some examples from the standard prelude. -- The list monad -- instance Monad [] where -- xs >>= f = concat (map f xs) -- return x = [x] -- zero = [] -- The Maybe monad -- instance Monad Maybe where -- (Just x) >>= k = k x -- Nothing >>= k = Nothing -- return = Just -- The identity monad data Id a = Id a instance Monad Id where return = Id (>>=) (Id x) f = f x -- The parsing monad -- data SParse a b = SParse (Parse a b) -- instance Monad (SParse a) where -- return x = SParse (succeed x) -- zero = SParse fail -- (SParse pr) >>= f -- = SParse (\s -> concat [ sparse (f x) rest | (x,rest) <- pr st ]) -- sparse :: SParse a b -> Parse a b -- sparse (SParse pr) = pr -- A state monad (the state need not be a table; this example is designed -- to support the example discussed below.) type Table a = [a] data State a b = State (Table a -> (Table a , b)) instance Monad (State a) where return x = State (\tab -> (tab,x)) (State st) >>= f = State (\tab -> let (newTab,y) = st tab (State trans) = f y in trans newTab) -- Example: Monadic computation over trees -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- A type of binary trees. data Tree a = Nil | Node a (Tree a) (Tree a) -- Summing a tree of integers -- A direct solution: sTree :: Tree Int -> Int sTree Nil = 0 sTree (Node n t1 t2) = n + sTree t1 + sTree t2 -- A monadic solution: first giving a value of type Id Int ... sumTree :: Tree Int -> Id Int sumTree Nil = return 0 sumTree (Node n t1 t2) = do num <- return n s1 <- sumTree t1 s2 <- sumTree t2 return (num + s1 + s2) -- ... then adapted to give an Int solution sTree' :: Tree Int -> Int sTree' = extract . sumTree -- where the value is extracted from the Id monad thus: extract :: Id a -> a extract (Id x) = x -- Using a state monad in a tree calculation -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- The top level function ... numTree :: Eq a => Tree a -> Tree Int -- ... and the function which does all the work: numberTree :: Eq a => Tree a -> State a (Tree Int) -- Its structure mirrors exactly the structure of the earlier program to -- sum the tree. numberTree Nil = return Nil numberTree (Node x t1 t2) = do num <- numberNode x nt1 <- numberTree t1 nt2 <- numberTree t2 return (Node num nt1 nt2) -- The work of the algorithm is done node by node, hence the function numberNode :: Eq a => a -> State a Int numberNode x = State (nNode x) nNode :: Eq a => a -> (Table a -> (Table a , Int)) nNode x table | elem x table = (table , lookup x table) | otherwise = (table++[x] , length table) -- -- Looking up a value in the table; will side-effect the table if the value -- is not present. lookup :: Eq a => a -> Table a -> Int lookup = lookup -- dummy definition: -- exercise for the reader -- Extracting a value froma state monad. extractSt :: State a b -> b extractSt (State st) = snd (st []) -- The top-level function defined eventually. numTree = extractSt . numberTree