Skip to content

foldM

Finally, let's look at the effectful sibling of foldl, called foldM. We can use foldl to add a bunch of numbers:

GHCi
>>> foldl (+) 0 [1..10]
55

But this doesn't work if the function we use to combine the values in the list returns a value decorated by some monad. In this case, we need

foldM :: Monad m => (b -> a -> m b) -> b -> [a] -> m b
foldM f = go
  where
    go accum []     = return accum
    go accum (x:xs) = do
        accum' <- f accum x
        go accum' xs

Just as foldl, foldM starts with an initial accumulator value accum. It then "adds" each list element to this accumulator value using the provided function f. However, f decorates its return value using some monad m, and foldM threads the effects of the applications of f to all list elements through the entire computation.

As a really simple example, consider summing up a list of numbers. Only they aren't really numbers, they are Maybe numbers. If all entries in the list are Just numbers, we want the result of summing them up to be Just the sum of all the numbers in the list:

GHCi
>>> sumMaybes [Just 3, Just 4, Just 7, Just 0]
Just 14

If there's at least one Nothing in the list, we want the result to be Nothing.

GHCi
>>> sumMaybes [Just 3, Just 4, Nothing, Just 0]
Nothing

This function is really easy to implement using foldM, even though it takes some squinting to see how it works:

sumMaybes :: Num a => [Maybe a] -> Maybe a
sumMaybes = foldM (fmap . (+)) 0

Let's puzzle this apart. Since the monad we use to decorate the result of sumMaybes is the Maybe monad, the accumulator has type a, and the list elements are of type Maybe a, we need the function passed to foldM as the first argument to have the type a -> Maybe a -> Maybe a. If the arguments are x and Just y, we want the result to be Just (x + y). If the arguments are x and Nothing, we want the result to be Nothing: The whole fold fails once we encounter the first Nothing in the list to be folded. By applying (+) to our accumulator value—let's call it total—we obtain the function (total +), which has the type a -> a. It returns the result of adding its argument to total. Applying fmap . (+) to total produces the function fmap (total +), which has the type Maybe a -> Maybe a. It maps Nothing to Nothing and Just x to Just (total + x). Thus, if the second argument of fmap . (+) is Just x, the new accumulator value is total + x. If the second argument is Nothing, the result is Nothing and the fold fails. This is exactly the behaviour we want. Try it out:

GHCi
>>> sumMaybes = foldM (fmap . (+)) 0
>>> sumMaybes [Just 3, Just 4, Just 7, Just 0]
Just 14
>>> sumMaybes [Just 3, Just 4, Nothing, Just 0]
Nothing