I have some understanding of list comprehension. I understand that the expression:
[x * x | x <- [1..10]]
should output [1,4,9,16,25,36,49,64,81,100]
The Haskell Report tells us how to translate list comprehensions:
[ e | True ] = [e] [ e | q ] = [ e | q, True ] [ e | b, Q ] = if b then [ e | Q ] else [] [ e | p <- l, Q ] = let ok p = [ e | Q ] ok _ = [] in concatMap ok l [ e | let decls, Q ] = let decls in [ e | Q ]
Since your list comprehension only uses irrefutable patterns (that is, patterns that never fail), the fourth clause above simplifies somewhat:
[ e | p <- l, Q ] = concatMap (\p -> [ e | Q ]) l
I'll use this version for concision, but a true derivation should use the definition from the Report. (Homework: try the real translation, and check that you get the "same thing" in the end.) Let's try it, shall we?
[(x,y+z) | x <- [1..10], y <- [1..x], z <- [1..y]]
= concatMap (\x -> [(x,y+z) | y <- [1..x], z <- [1..y]] [1..10]
= concatMap (\x -> concatMap (\y -> [(x,y+z) | z <- [1..y]]) [1..x]) [1..10]
= concatMap (\x -> concatMap (\y -> [(x,y+z) | z <- [1..y], True]) [1..x]) [1..10]
= concatMap (\x -> concatMap (\y -> concatMap (\z -> [(x,y+z) | True]) [1..y]) [1..x]) [1..10]
= concatMap (\x -> concatMap (\y -> concatMap (\z -> [(x,y+z)]) [1..y]) [1..x]) [1..10]
And we're finally at a version that has no list comprehensions.
If you're comfortable with monads, then you can also gain insight into the behavior of this expression by observing that concatMap
is a flipped version of the list's (>>=)
function; moreover, [e]
is like the list's return e
. So, rewriting with monad operators:
= [1..10] >>= \x ->
[1..x] >>= \y ->
[1..y] >>= \z ->
return (x,y+z)
You can transform this into do
-notation:
foo = do x <- [1..10]
y <- [1..x]
z <- [1..y]
return (x, y+z)
This works because list is a monad. The do
-notation itself is just syntactic sugar for a monadic calculation. Following the desugaring rules (described here under "Desugaring of do blocks") you end up with:
[1..10] >>= (\x -> [1..x] >>= (\y -> [1..y] >>= (\z -> [(x,y+z)])))
The operator >>=
is defined in Control.Monad
, and is equivalent to a concatMap
with flipped arguments for lists. return t
is just [t]
in case of lists.
[(x,y+z) | x <- [1..10], y <- [1..x], z <- [1..y]]
is the same as
concatMap
(\x -> [(x,y+z) | y <- [1..x], z <- [1..y]])
[1..10]
You can extract the y
and z
variables out of the list comprehension similarly. (But you must do it in order from left to right: so y
next and z
last.)
concatMap
is a function defined in the Prelude:
concatMap :: (a -> [b]) -> [a] -> [b]
concatMap f = concat . map f