Overview Declarative Languages D7012E: General monads in haskell Fredrik Bengtsson IO-monad sequencing operator monad class requirements on monad Monadic computation trivial example useful example The old IO monad remember do-notation (sequencing): getnput :: IO () getnput = do line <- getline putstrln line only for handling I/O can we handle other side-effects? Why would we like to do that? might suit problem better functional style not good for everything General monads IO a comes with several functions: return :: a -> IO a putstr :: String -> IO () getline :: IO String sequencing using do construct sequencing operator Now, we will look at the theory......at least a little bit Sequencing operator >>= pronounced "then" used to sequence two operations >>= :: IO a -> (a -> IO b) -> IO b result of action (state) IO a feeds a into second argument function new action returns result from second argument function new state sequencing two actions Sequencing operator example do-notation addoneint :: IO () addoneint = do line <- getline putstrln (show (1 + read line :: Int)) sequencing operator notation: addoneint = getline >>= \line -> putstrln (show (1 + read line :: Int)) do is a shorthand for >>= I/O a I/O b 1
Monadic style programming monads makes sequencing explicit sequencing operator (for monad m): (>>=) :: m a -> (a -> m b) -> m b necessary? consider inputint - inputint order important rewrite as do e <- getint f <- getint return (e-f) looks like an imperative program can be incorporated into a pure functional language The Monad Class The definition of the Monad class class Monad m where (>>=) :: m a -> (a -> m b) -> m b return :: a -> m a (>>) :: m a -> m b -> m b fail :: String -> m a Informal requirements on a monad return x return value without effect (just add monad type) without I/O in case of IO monad sequencing from >>= irrelevant of way of bracketing fail s fails with message s Composition operator new operator easier to read (>@>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c) f >@> g = \x -> ((f x) >>= g) generalizes function composition composes two objects into one compose a -> m b and b -> m c into a -> m c effect of performing first and second action Requirements on monad return is identity 1: return >@> f = f 2: f >@> return = f 3: >@> associative (f >@> g) >@> h = f >@> (g >@> h) derived operator >> also associative Equivalent requirements ok, back to reality... since m >>= f = do x <- m f x remove monad type feed value into f gives new monad type the first two rules becomes do y <- return x (equivalent to f x) f y do x <- m (equivalent to m) return x Third rule is implicit in the do construct do is associative Simplest example: m >>= f = f m The identity monad return = id >@>becomes forward composition of functions, >.> represents trivial state no action performed value immediately returned 2
Two standard functions mapf :: Monad m => (a -> b) -> m a -> m b joinm :: Monad m => m (m a) -> m a mapf f m = do x <- m return (f x) joinm m = do x <- m x tree data type data Tree a = Nil Node a (Tree a) (Tree a) compute sum of integers in tree direct recursive solution: stree :: Tree Int -> Int stree Nil = 0 stree (Node n t1 t2) = n + stree t1 + stree t2 sumtree :: Tree Int -> St Int Stis a monad (not defined yet) sumtree Nil = return 0 sumtree (Node n t1 t2) = do num <- return n s1 <- sumtree t1 s2 <- sumtree t2 return (num + s1 + s2) No special side-effects use identity monad data St a = Id a instance Monad Id where return = Id (>>=) (Id x) f = f x thus, we could say sumtree :: Tree Int -> Id Int we would like the type Tree Int -> Int define extract :: Id a -> a extract (Id x) = x now extract. sumtree What s the point? Functional style would have been easier There s no point we don t use any side-effects Isn t side-effects a bad idea in general? yes, but it can be unavoidable this is a more controlled environment Another example... 3
State in monadic computation consider assigning integer to all values in tree same integer for same value we would like numtree :: Eq a => Tree a -> Tree Int how to do? we define numbertree :: Eq a => Tree a -> State a (Tree Int) monad type State adependent on tree type Tree a» monad for this type of tree State in monadic computation numbertree could look like this numbertree Nil = return Nil numbertree (Node x t1 t2) = do num <- numbernode x nt1 <- numbertree t1 nt2 <- numbertree t2 return (Node num nt1 nt2) A type for a table type Table a = [a] A state containing the table data State a b = State (Table a -> (Table a, b)) interpretation: takes a Table returns a b updates Table as side-effect The state monad instance Monad (State a) where return x = State (\tab -> (tab,x)) leave state unchanged, just return value (State st) >>= f = State (\tab -> let (newtab,y) = st tab (State trans) = f y in trans newtab) Just a reminder: data State a b = State (Table a -> (Table a, b)) (>>=) :: m a -> (a -> m b) -> m b interpretation of >>= we want to sequence st with f perform st(pass tab to st) new state: newtab(after st) and value y pass yto f get new State pass newtabto this new state function State in monadic computation 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) if x in table new int retrive integer from table otherwise add new integer to table 4
State in monadic computation lookup :: Eq a => a -> Table a -> Int extractst :: State a b -> b extractst (State st) = snd (st []) function st applied to initial state [] returns pair take second part Int we're looking for numtree = extractst. numbertree Monadic style programming Incorporates imperative (monadic) style into pure functional languages Without poisoning other code theory complex fairly easy to use Next lecture functional style in imperative languages 5