It's easy to see when re-writing the code with bind and return:
[1,2] >>= (\x-> [x,x+1]) === concatMap (\x-> [ x,x+1 ]) [1,2]
[1,2] >>= (\x-> return [x,x+1]) === concatMap (\x-> [ [x,x+1] ]) [1,2]
Your first code is tantamount to calling join
on the results of the second, removing one monadic "layer" introduced by return :: a -> m a
, conflating the "list" of the monad in use, with the "list" of your value. If you were returning a pair, say, it wouldn't have made much sense to omit the return
:
-- WRONG: type mismatch
[1,2] >>= (\x-> (x,x+1)) === concatMap (\x-> ( x,x+1 )) [1,2]
-- OK:
[1,2] >>= (\x-> return (x,x+1)) === concatMap (\x-> [ (x,x+1) ]) [1,2]
Or, we can use a join/fmap
re-write:
ma >>= famb === join (fmap famb ma) -- famb :: a -> m b, m ~ []
join (fmap (\x-> [x,x+1]) [1,2]) = concat [ [ x,x+1 ] | x<-[1,2]]
join (fmap (\x-> (x,x+1)) [1,2]) = concat [ ( x,x+1 ) | x<-[1,2]] -- WRONG
join (fmap (\x-> return [x,x+1]) [1,2]) = concat [ [ [x,x+1] ] | x<-[1,2]]
= [y | x<-[1,2], y<-[ x,x+1 ]]
{- WRONG -} = [y | x<-[1,2], y<-( x,x+1 )]
= [y | x<-[1,2], y<-[[x,x+1]]]
return
in theMonad
class with thereturn
keyword in C-like imperative languages. They are completely different, but quite a lot of people used to those languages seereturn
and don't realise that it's not causing a value to be returned, it's making a computation which will return that value when evaluated at some later point. Being a functional language, Haskell has no need for anything resembling areturn
statement. – Ronnieronnyreturn
was, imho, a huge mistake. But due to backwards compatibility, there's no going back on it now! – Octando_not_return a = return a
– Naganopure
. – Brickerpure
" is also a bad name.pure x
implies taking anx
and making it "pure" (likesin x
takesx
and returns the sine ofx
). but in fact it's the opposite: it takes a "pure"x
. – Lodenf1 = do { x <- [1,2] ; [x, x+1] } = do { x <- [1,2] ; r <- [x, x+1] ; return r} = join (do { x <- [1,2] ; return [x, x+1] }) = concat f2
. – Loden