[List monad intro. Bryan O'Sullivan **20071221070110] { addfile ./examples/ch14/ListMonad.hs addfile ./examples/ch14/listmonad.ghci hunk ./en/ch14-monads.xml 699 - that might fail. + that might not produce a result. hunk ./en/ch14-monads.xml 801 + + + + The list monad + + While the Maybe type can represent either no + value or one one, there are many situations where we might want + to return more than one result. Obviously, a list is well + suited to this purpose. The type of a list suggests that we + might be able to use it as a monad, because its type constructor + has one free variable. And sure enough, we can use a list as a + monad. + + Rather than simply present the Prelude's Monad + instance for the list type, let's try to figure out what the + instance ought to look like. This is easy + to do: we'll look at the types of &bind; and &return;, and + perform some substitutions, and see if we can use a few familiar + list functions. + + The more obvious of the two functions is &return;. We know + that it takes a type a, and wraps + it in a type constructor m to + give the type m a. We also know + that the type constructor here is []. Substituting + this type constructor for the type variable m gives us the type [] a + (yes, this really is valid notation!), which we can rewrite in + more familiar form as [a]. + + We now know that &return; for lists should have the type + a &larrow; [a]. There are only a few sensible + possibilities for an implementation of this function. It might + return the empty list, a singleton list, or an infinite list. + The most appealing behaviour, based on what we know so far about + monads, is the singleton list: it doesn't throw information + away, nor does it repeat it infinitely. + + &ListMonad.hs:returnSingleton; + + If we perform the same substitution trick on the type of + &bind; as we did with &return;, we discover that it should have + the type [a] &rarrow; (a &rarrow; [b]) &rarrow; + [b]. This seems close to the type of + map. + + &listmonad.ghci:map; + + The ordering of the types in map's + arguments doesn't match, but that's easy to fix. + + &listmonad.ghci:flipMap; + + We've still got a problem: the second argument of flip + map has the type a &rarrow; b, whereas the + second argument of &bind; for lists has the type a + &rarrow; [b]. What do we do about this? + + Let's do a little more substitution and see what happens + with the types. The function flip map can return + any type b as its result. If we + substitute [b] for b in both places where it appears in + flip map's type signature, its type signature reads + as a &rarrow; (a &rarrow; [b]) &rarrow; [[b]]. In + other words, if we map a function that returns a list over a + list, we get a list of lists back. + + &listmonad.ghci:flipMapApp; + + Interestingly, we haven't really changed how closely our + type signatures match. The type of &bind; is [a] &rarrow; + (a &rarrow; [b]) &rarrow; [b], while that of flip + map when the mapped function returns a list is + [a] &rarrow; (a &rarrow; [b]) &rarrow; [[b]]. + There's still a mismatch in one type term; we've just moved that + term from the middle of the type signature to the end. However, + our juggling wasn't in vain: we now need a function that takes a + [[b]] and returns a [b], and one + readily suggests itself in the form of + concat. + + &listmonad.ghci:concat; + + The types suggest that we should flip the arguments to + map, then concat the + results to give a single list. + + &listmonad.ghci:bind; + + This is exactly the definition of &bind; for lists. + + &ListMonad.hs:instance; + + With the two core definitions in hand, the implementations of + the non-core definitions that remain, &bind_; and &fail;, ought + to be obvious. + + &ListMonad.hs:rest; + hunk ./examples/ch14/ListMonad.hs 1 +{-- snippet returnSingleton --} +returnSingleton :: a -> [a] +returnSingleton x = [x] +{-- /snippet returnSingleton --} + +{-- snippet instance --} +instance Monad [] where + return x = [x] + xs >>= f = concat (map f xs) +{-- /snippet instance --} +{-- snippet rest --} + xs >> f = concat (map (\_ -> f) xs) + fail _ = [] +{-- /snippet rest --} hunk ./examples/ch14/listmonad.ghci 1 +--# map +:type (>>=) +:type map + +--# flipMap +:type (>>=) +:type flip map + +--# flipMapApp +flip map [1,2,3] (\a -> [a,a+100]) + +--# concat +:type concat + +--# bind +:type \xs f -> concat (map f xs) }