[More about the list monad Bryan O'Sullivan **20071222064827] { addfile ./examples/ch14/CartesianProduct.hs addfile ./examples/ch14/cartesian.ghci hunk ./en/ch14-monads.xml 895 - With the two core definitions in hand, the implementations of - the non-core definitions that remain, &bind_; and &fail;, ought - to be obvious. + It applies f to every element in the list + xs, and concatenates the results to return a + single list. + + With our two core Monad definitions in hand, + the implementations of the non-core definitions that remain, + &bind_; and &fail;, ought to be obvious. hunk ./en/ch14-monads.xml 905 + + Understanding the list monad + + The list monad is similar to a familiar Haskell tool, the + list comprehension. We can illustrate the similarity by + computing the Cartesian product of two lists. First, we'll + write a list comprehension. + + &CartesianProduct.hs:comprehensive; + + For once, we'll use explicit notation for the monadic code + instead of block notation. This will highlight how + structurally similar the monadic code is to the list + comprehension. + + &CartesianProduct.hs:monadic; + + The only real difference is that the value we're + constructing comes at the end of the sequence of expressions, + instead of the beginning as in the list comprehension. Also, + the results of the two functions are identical. + + &cartesian.ghci:comparison; + + It's easy to be baffled by the list monad early on, so + let's walk through our monadic Cartesian product code again in + more detail. This time, we'll rearrange the function to use + layout instead of explicit notation. + + &CartesianProduct.hs:blockyDo; + + For every element in the list xs, the + rest of the function is evaluated once, with + x bound to a different value from the list + each time. Then for every element in the list + ys, the remainder of the function is + evaluated once, with y bound to a different + value from the list each time. + + What we really have here is a doubly nested loop! This + highlights an important fact about monads: you + cannot predict how a block of monadic + code will behave unless you know what monad it will execute + in. + + We'll now walk through the code even more explicitly, but + first let's get rid of the &do; notation, to make the + underlying structure clearer. We've indented the code a + little unusually to make the loop nesting more obvious. + + &CartesianProduct.hs:blockyPlain; + + If xs has the value + [1,2,3], the two lines that follow are evaluated + with x bound to 1, then to + 2, and finally to 3. If + ys has the value [True, + False], the final line is evaluated + six times: once with x + as 1 and y as + True; again with x as + 1 and y as False; + and so on. The &return; expression wraps each tuple in a + single-element list. + hunk ./en/ch14-monads.xml 1093 + + + Why go sugar-free? + + When we write &bind; explicitly in our code, it reminds us + that we're calling stitching functions together using + combinators, not simply sequencing actions. + + As long as you feel like a novice with monads, we think + you should prefer to explicitly write &bind; over the + syntactic sugar of &do; notation. The repeated reinforcement + of what's really happening seems, for many programmers, to + help to keep things clear. (It can be easy for an imperative + programmer to relax a little too much from exposure to the + IO monad, and assume that a &do; block means + nothing more than a simple sequence of actions.) + + Once you're feeling more familiar with monads, you can + choose whichever style seems more appropriate for writing a + particular function. Indeed, when you read other people's + monadic code, you'll see that it's unusual, but by no means + rare, to mix both &do; notation and + &bind; in a single function. + hunk ./examples/ch14/CartesianProduct.hs 1 +{-- snippet comprehensive --} +comprehensive xs ys = [(x,y) | x <- xs, y <- ys] +{-- /snippet comprehensive --} + +{-- snippet monadic --} +monadic xs ys = do { x <- xs; y <- ys; return (x,y) } +{-- /snippet monadic --} + +{-- snippet blockyDo --} +blockyDo xs ys = do + x <- xs + y <- ys + return (x, y) +{-- /snippet blockyDo --} + +{-- snippet blockyPlain --} +blockyPlain xs ys = + xs >>= + \x -> ys >>= + \y -> return (x, y) +{-- /snippet blockyPlain --} hunk ./examples/ch14/cartesian.ghci 1 +:load CartesianProduct + +--# comparison +comprehensive [1,2] "bar" +comprehensive [1,2] "bar" == monadic [1,2] "bar" }