For a college assignment I am learning Haskell and when reading about the do-notation and sequencing with >>= and >> I came across this behaviour that I did not expect.
[1,2,3] >> [1] -- returns [1,1,1]
Can anyone explain why every element of the first array is replaced by the elements of the second array? It seems like the list is concatenated in some way, while I expected the result of the first expression to be completely ignored, thus I would expect "[1]" as the result.
Thanks a lot in advance.
CodePudding user response:
The “result” is in this case the values contained in [1,2,3]
, which are indeed ignored. What >>
does not ignore is the context, which for the list monad is the shape (i.e. length) of the list. This can't be ignored, because we must have x >>= pure ≡ x
, i.e.
Prelude> [1,2,3] >>= pure
[1,2,3]
Prelude> [1,2,3] >>= \n -> [n]
[1,2,3]
Prelude> [1,2,3] >>= \n -> [1]
[1,1,1]
Prelude> [1,2,3] >>= \_ -> [1]
[1,1,1]
Prelude> [1,2,3] >> [1]
[1,1,1]
An example with length>1 on the RHS:
[1,2,3] >>= \n -> [n, n 10]
[1,11,2,12,3,13]
Prelude> [1,2,3] >>= \n -> [100, 10]
[100,10,100,10,100,10]
Prelude> [1,2,3] >> [100, 10]
[100,10,100,10,100,10]
CodePudding user response:
There are several equivalent ways of writing [1,2,3] >> [1]
:
do [1,2,3]
return 1
[ x | _ <- [1,2,3], x <- [1] ]
[1,2,3] >>= \_ -> [1]
concatMap (const [1]) [1,2,3]
concat (map (const [1]) [1,2,3])
concat ([1,2,3] $> [1])
It replaces every element of [1..3]
with [1]
and then collapses it:
concatMap (\_ -> [1]) [1,2,3]
= concat (map (\_ -> [1]) [1,2,3])
= concat [[1],[1],[1]]
= [1,1,1]
It completely ignores the elements of [1,2,3]
, just using the shape (length). Look what happens if we replace them with undefined
:
> do [undefined, undefined, undefined]; return 1
[1,1,1]