Topic: More Monads Date: Nov. 30, 2009 Number: 28 Examples: QueensM.hs, ParserM.hs Reading: Chapter 18 -- List Monad Another monad is the List monad. The definition may seem strange at first, but it turns out to be very useful. instance Monad [] where m >>= k = concat (map k m) return x = [x] fail x = [ ] Well, return certainly makes sense - the obvious way to turn an element into a list of elements is to make it a singleton list. Having fail return an empty list seems reasonable - kind of the list equivalent of "Nothing". But why should bind be concat of a map? If we are binding m >>= k then m is a list, maybe [a]. k should be a function a -> [b]. How can we apply a function that takes an "a" to a [a]? One reasonable option is to apply it to every item in [a]. But then we end up with [[b]], becase k :: a -> [b]. So how do we get back to just [b] that the bind should return? The concat will do this. Why would we want this? First, consider: do x <- [1,2,3] return (x * 2) which translates to: [1,2,3] >>= \x -> do return (x * 2) => (elim singleton do) [1,2,3] >>= \x -> return (x * 2) => (unfold >>=) concat (map (\x -> return (x * 2)) [1,2,3]) => (unfold return) concat (map (\x -> [x * 2]) [1,2,3]) => concat [[2],[4],[6]] => [2,4,6] Try on your own: do x <- [1,2,3] y <- [4,5,6] return (x,y) Should get: [(1,4), (1,5), (1,6), (2,4), (2,5), (2,6), (3,4), (3,5), (3,6)] Note that the first is exactly what you would get from the list comprehension: [x * 2 | x <- [1,2,3]] and the second is: [(x,y) | x <- [1,2,3], y <- [4,5,6]] So list comprehensions are nothing but syntactic sugar for list monad operations! Remember that we used list comprehensions in nQueens to generate all possible next queen placements. We could instead have used the List Monad to do the same thing. placeQueens :: Int -> [[Int]] -> [[Int]] placeQueens 0 solutions = solutions placeQueens col partials = do row <- [1 .. n] partial <- partials let possibles = return (row:partial) placeQueens (col-1) (filter firstIsValid possibles) -- IO Monad Let's review what we know of the IO monad, to see how this applies and to understand more about what is happening. Things within the IO monad are actions. (They are not normal functions, because they do not always behave the same way on the same parameters.) They are things like: print putStrLn getLine They all have type IO a. IO is the type of the IO monad. We have seen things like: do let msg = "Enter your name" putStr msg name <- getLine putStrLn ("Hello, " ++ name) We see that the let statement is not really a normal let. It establishes msg as a name for the rest of the "do". It is translated to: let msg = "Enter your name" in do putStr msg name <- getLine putStrLn ("Hello, " ++ name) So we have a clean way to deal with "let". Note if you used a variable name in successive lets, the second would shadow the first and the first could no longer be seen. Note that the only relationship between: putStr msg name <- getLine is that the putStr must occur before the getLine. What we care about here is sequencing. So we use the sequence operator >>. Thus putStr msg >> name <- getLine is equivalent to do putStr msg name <- getLine The <- is a bit different. If the right side is IO a, the left side is just a. It somehow performs the IO action and makes the resulting value available for later use. To be exact, it evaluates the right side (getLine in this case), binds the String value returned to name, and makes name available in the rest of the do. This is why the >>= operator is called "bind". However, while this "take the value out of the monad and bind it" makes sense in the IO monad and Maybe mondad, it is misleading in the List monad. We should understand that <- actually is translated to >>=, and that exactly how it works is dependent on how bind is implemented. -- MonadPlus There is a further extension of Monad called MonadPlus, which requires a "plus" operation and a zero element: class Monad m => MonadPlus m where mzero :: m a mplus :: m a -> m a -> m a The appropriate laws are: m `mplus` mzero = m mzero `mplus` m = m We first look at MonadPlus for Maybe: instance MonadPlus Maybe where mzero = Nothing Nothing `mplus` x = x x `mplus` _ = x Note that this satisfies the laws. If either argument to mplus is Nothing we return the other. (If both are Just, we return the first.) This allows us to extend our sheep example to find a parent if one exists: -- returns a parent, if one exists parent :: Sheep -> Maybe Sheep parent s = (mother s) `mplus` (father s) Getting a grandparent is a bit trickier: -- returns a grandparent, if one exists. grandparent :: Sheep -> Maybe Sheep grandparent s = (mother s >>= parent) `mplus` (father s >>= parent) It would seem natural to define it: grandparent x = return s >>= parent >>= parent Why does this sometimes fail? (Mother may get returned from first parent call, but she may have no parent. But the father may. In this version we will never look at the father if the mother exists.) Another example is lists, where the "plus" operation is (++): instance MonadPlus [] where mzero = [] mplus = (++) It seems natural to think of ++ as addition of lists. In that case, the list that works as mzero has to be [], because appending it to a list returns the same list. In these contexts, the "bind" operation can usually be thought of as "multiply". In particular, bindings that involve mzero end up evaluating to mzero. Note if we bind with Nothing in Maybe we get Nothing. (Look at definition and verify that this is true.) Binding with an empty list []>>=f gives [], which is mzero for lists. Similarly, m>>[] will map [] over the things in m, and then will concat these empty lists. So this works also. -- Utility functions Book gives a number of Monadic utility functions: sequence, sequence_, mapM, mapM_, and =<<. We now see that the underscore just means return () rather than the value computed by sequence or mapM. In particular, sequence_ :: Monad m => [m a] -> m () sequence_ = foldr (>>) (return ()) and mapM_ :: Monad m => (a -> m b) -> [a] -> m () mapM_ f as = sequence_ (map f as) -- State monads Our last example of the use of monads is to maintain state. Consider the parser. We defined Parser: type Parser a = String -> Maybe (a, String) So a parser is a function from an input string to a result followed by the remaining string. (Well, Maybe it is.) The current input string is the state that is being passed. We used #, >->, etc. to do pass state "under the hood". In fact, we can use a monad to do the same thing. First, to make Parser an instance of Monad we have to make it a true data type with a constructor: data Parser a = Parser (String -> Maybe (a, String)) This will make life a bit trickier, because instead of returning a function (which can be applied directly to a string) we get something of the form: Parser parseFunction To make our life easier, we write a function: runParser :: Parser a -> String -> Maybe (a, String) runParser (Parser parseFunction) str = parseFunction str which gets the parseFunction and applies it. Making Parser an instance of Monad is straightforward for what return and fail should be: they are pretty much exactly what returnParse and failParse were in the original parser. instance Monad Parser where return value -- equivalent to returnParse in Parser = Parser (\str -> Just (value, str)) fail string -- equivalent to failParse in Parser = Parser (\str -> Nothing) parser >>= function = Parser $ \ firstString -> case runParser parser firstString of Nothing -> Nothing Just (intermediateValue, intermediateString) -> runParser (function intermediateValue) intermediateString The tricky one is bind. It should bind a (Parser a) with a function ( a -> Parser b). But where will this function come from, and why do we need to pass a as a parameter? This becomes clearer when we see how this will eventually be used. We will later see that (>->) is defined as: (>->) :: Parser a -> (a->b) -> Parser b m >-> f = do a <- m return (f a) This translates to: m >>= \a -> return (f a) So the value returned by m is passed on as a parameter in an anonymous function, so it is available in the parser "return (f a)". This has a lot of similarities to what # does. It needs to return a Parser, so we start with the constructor Parser whose argument is an anonymous function whose parameter is the input string. We then run that parser on firstString, just as # did. If it returns Nothing we return Nothing, just as # did. But what if it did not? In # we ran the second parser on the intermediate string returned. But here we don't have a second parser. We have function :: a -> Parser b How do we get the second parser? We call function on intermediateValue. It returns a Parser b. (In the example above, this gives the Parser "return (f intermediateValue)", which is what we want.) We then run this parser on intermediateString and return the result. So note that two things get passed on to the second parser: 1) The value returned by the first parser (as the argument to the parameter of a function that takes that argument and returns the second parser with that argument filled in). 2) The remainder of the input string, passed as the parameter of (function intermediateValue). We also make Parser an instance of MonadPlus: instance MonadPlus Parser where mzero = Parser (\str -> Nothing) x `mplus` y = Parser (\str -> runParser x str `mplus` runParser y str) Basically we use the same defintion as for Maybe, but for functions that return Maybe. We run the two parsers and `mplus` their results. -- Sequence operations The sequence operations are now easily defined using do's: infixl 6 # (#) :: Parser a -> Parser b -> Parser (a,b) m # n = do a <- m b <- n return (a,b) On the next one we don't need the value for m, so we don't bind it. -- Sequence throwing away first or second part (whichever has '-') -- (Old definitions using >-> would also work.) infixl 6 -# (-#) :: Parser a -> Parser b -> Parser b m -# n = do m n infixl 6 #- (#-) :: Parser a -> Parser b -> Parser a m #- n = do a <- m n return a -- Parses two things and conses their results into one list infixl 6 #: (#:) :: Parser a -> Parser [a] -> Parser [a] m #: n = do a <- m b <- n return (a:b) -- Parses two things and appends their results into one list infixl 6 #++ (#++) :: Parser [a] -> Parser [a] -> Parser [a] m #++ n = do a <- m b <- n return (a ++ b) The other operations are also simplified: -- Transforms the output of a parse by applying a function infix 5 >-> (>->) :: Parser a -> (a->b) -> Parser b m >-> f = do a <- m return (f a) -- Parses using m, but only succeeds if m succeeds and p is true. -- Note that the way >>= is defined if m returns Nothing the whole do -- will return Nothing, so it is safe to try to evaluate (p a). infix 7 ? (?) :: Parser a -> (a -> Bool) -> Parser a m ? p = do a <- m if p a then return a else fail "predicate false" -- Alternatives - Finds if either succeeds. If both succeed, choose -- first. -- This uses "mplus" from MonadPlus infixl 3 ! (!) :: Parser a -> Parser a -> Parser a m ! n = m `mplus` n The thing that becomes a bit trickier is the char parser: -- Parses a single character char :: Parser Char char = Parser $ \cs -> case cs of "" -> Nothing (c:cs) -> Just (c,cs) We have to build up our parser "by hand" using a constructor and an anonymous function. (Actually, there is an alternative that we will see next time.) Because of this it is easier to define lit in terms of char: -- Parse a single character passed as parameter c lit :: Char -> Parser Char lit c = char ? (== c)