List comprehensions (in fact, Monad comprehensions) can be desugared into do
notation.
do i <- [1..4]
j <- [i+1..4]
return (i,j)
Which can be desugared as usual:
[1..4] >>= \i ->
[i+1..4] >>= \j ->
return (i,j)
It is well known that a >>= \x -> return b
is the same as fmap (\x -> b) a
. So an intermediate desugaring step:
[1..4] >>= \i ->
fmap (\j -> (i,j)) [i+1..4]
For lists, (>>=) = flip concatMap
, and fmap = map
(flip concatMap) [1..4] (\i -> map (\j -> (i,j) [i+1..4])
flip
simply switches the order of the inputs.
concatMap (\i -> map (\j -> (i,j)) [i+1..4]) [1..4]
And this is how you wind up with Tsuyoshi’s answer.
The second can similarly be desugared into:
concatMap (\i ->
concatMap (\j ->
map (\k ->
(i,j,k))
[j+1..6])
[i+1..6])
[1..6]