Search code examples
listhaskellmonadsdo-notation

Using return vs. not using return in the list monad


I started my Grand Haskell Crusade (GHC :) ) and I am a bit confused with monads and IO functions. Could anyone explain simply what is the difference between those two functions?

f1 = do x <- [1,2]
        [x, x+1] -- this is monad, right?

f2 = do x <- [1,2]
        return [x, x+1]

The results are:

*Main> f1
[1,2,2,3]

*Main> f2
[[1,2],[2,3]]

Solution

  • The other answers here are correct, but I wonder if they're not quite what you need... I'll try to keep this as simple as possible, just two points:


    Point 1. return is not a special thing in the Haskell language. It's not a keyword, and it's not syntactic sugar for something else. It's just a function that's part of the Monad typeclass. Its signature is simply:

    return :: a -> m a
    

    where m is whichever monad we're talking about at the time. It takes a "pure" value and jams it into your monad. (Incidentally, there's another function called pure that's basically a synonym for return... I like it better because the name is more obvious!) Anyway, if m is the list monad, then return has this type:

    return :: a -> [a]
    

    If it helps, you could think of the type synonym type List a = [a], which might make it slightly more obvious that List is the thing we're substituting for m. Anyway, if you were going to implement return yourself, the only reasonable way you'd implement it is by taking some value (of whatever type a) and sticking it in a list by itself:

    return a = [a]
    

    So I can say return 1 in the list monad, and I'll get [1]. I can likewise say return [1, 2, 3] and I'll get [[1, 2, 3]].


    Point 2. IO is a monad, but not all monads are IO. Many Haskell tutorials seem to conflate the two topics largely for historical reasons (incidentally, the same confusing historical reasons that led to return being so poorly named). It sounds like you might have some (understandable) confusion around that.

    In your code, you're in the list monad because you wrote do x <- [1, 2]. If instead you had written do x <- getLine for example, you'd be in the IO monad (because getLine returns IO String). Anyway, you're in the list monad, so you get list's definition of return described above. You also get list's definition of >>=, which is just (a flipped version of) concatMap, defined as:

    concatMap :: (a -> [b]) -> [a] -> [b]
    concatMap f xs = concat (map f xs)
    

    The other posted answers pretty much have it covered from here :) I know I didn't answer your question directly, but I'm hoping these two points instead addressed the fundamental things you might have found confusing.