listfunctionhaskellmonads

List instance of monad in Haskell - why use concat in bind-operation?


I found some question here: Redefining monad list instance. I'm currently trying to get my head wrapped around monads. But I need some help here, I don't get the instance-definition of lists as monads.

This is my given definition of a list-instance for a monad:

instance Monad [] where
    xs >>= f = concat $ map f xs
    return x = [x]
    fail _ = []

I dont understand, why I need concat in the bind-function. This is the signature of (>>=):

    (>>=) :: Monad m => m a -> (a -> m b) -> m b

So I have some monadic value m a and a function, taking a value a and producing a monadic value m b given as parameters. I 'feed' a from m a into the function (a -> m b) and thus get a monadic value m b as a result. In my own words: The bind-operator (>>=) allows to chain monadic functions (returning monadic values) where the value of the output of the earlier function is the input for the next function. Right?

Back to the list-instance. map f xs uses the function f on every value in xs. So map (*2) [1,2,3] results in [2,4,6]. And that's all I wanted here or not? How should I use concat here? The definition of concat is as follows:

    concat :: [[a]] -> [a]

Why do I get a list of lists in the (>>=)-function? Is it because list is the monad and I take every single value from that list to feed it to f and map just gets singleton-inputs? But how do I iterate over the whole list then? Where does the 'picking each value' happen? And if map takes the whole list xs as input (that's how I understand it) why should I get a list of lists?


Solution

  • If

    x :: [a]
    f :: a -> [b]
    

    then

    map f :: [a] -> [[b]]
    map f x :: [[b]]
    

    so, we need to flatten the latter into [b]. This is done by concat.

    Note how f was already producing a list, so map makes that into a list-of-lists. This is crucial: if f was not producing a list but were f :: a->b, then we don't need concat -- we don't even need a monad, since a functor providing fmap=map :: (a->b) -> [a] -> [b] would be enough.

    The added benefit of a monad over a functor mainly lies in letting f produce a value in a monadic type.