I think I do understand the list monad but then I found I am not. Here is the story.
Given list m
and function k
> let m = [1..10]
> :t m
m :: [Integer]
> let k = replicate 2
> :t k
k :: a -> [a]
Playing with bind >>=
give what I expect
> :t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b
> :t m >>= k
m >>= k :: [Integer]
> m >>= k
[1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10]
but for >>
Expected (from experiencing with IO monad, everything on left side would be discarded)
m >> m
[1,2,3,4,5,6,7,8,9,10]
Got
> :t (>>)
(>>) :: Monad m => m a -> m b -> m b
:t m >> m
m >> m :: [Integer]
> m >> m
[1,2,3,4,5,6,7,8,9,10,1,2,3,4,5 ... 9,10] -- truncated, real output is 100 elements
Please explain why >>
is not behave like I expected (of course I must have misunderstanding) and what is the correct way to interpret >>
?
Discards are placeholder variables that are intentionally unused in application code. Discards are equivalent to unassigned variables; they don't have a value.
C# Hidden Gems - Discards Variable (_)Discards are equal to unassigned variables. The purpose of this feature is to use this variable when you want to intentionally skip the value by not creating a variable explicitly.
(>>)
discards values from its first argument, but not effects. In this case it might be easier to see if you use lists with different types:
λ> "ab" >> [1,2,3,4]
[1,2,3,4,1,2,3,4]
Note how the values of the first list aren't used at all.
Remember the definition of (>>)
: a >> b = a >>= (\_ -> b)
. So this turns into "ab" >>= (\_ -> [1,2,3,4])
, i.e. concat (map (\_ -> [1,2,3,4]) ['a','b'])
, i.e. concat [[1,2,3,4],[1,2,3,4]]
(also, [i | _ <- "ab", i <- [1,2,3,4]]
).
With []
, (>>=)
means something like "for each". The function on the right gets as an argument each value on the left. So (>>)
, which discards values, still means "for each" -- but this time it can't use the value, so it just means "the elements of the second list, repeated as many times as there are elements in the first list".
foo >> bar
is the same as foo >>= \_ -> bar
. So in the case of IO it executes the left action, ignoring the return value of that action, and then executes the right action. In the case of lists it maps over each element in the left list, ignoring the value of each elements, and inserts the right list at each point.
Another way to look at it is that >>=
for lists is the same as flip concatMap
, >>
is the same as flip (concatMap . const)
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With