Skip to content

Lists

Having discussed the long list of functions provided by the Foldable type class, let's implement Foldable instances for a bunch of containers to understand better how folds work. We need to implement

foldl :: (b -> a -> b) -> b -> t a -> b

or

foldMap :: Monoid m => (a -> m) -> t a -> m

The former needs to fold up the container left to right. The latter needs to go in the opposite direction, from right to left. While it is sufficient to implement only one of them, we'll discuss implementations of both, for practice.

We've seen the implementation of foldl for lists before:

foldl f = go
  where
    go accum []     = accum
    go accum (x:xs) = go (f accum x) xs

Here's foldMap. Since it needs to fold the list right-to-left, its implementation is similar to that of foldr:

foldMap f = go
  where
    go []     = mempty
    go (x:xs) = f x <> go xs

For the empty list, the result has to be the unit of the monoid, mempty. For the non-empty list x:xs, we fold up the tail xs of the list recursively and then combine the monoid element f x with the result of go xs using the monoid multiplication <>.

Thus, we can define a Foldable instance for lists as

instance Foldable [] where
    foldl f = go
      where
        go accum []     = accum
        go accum (x:xs) = go (f accum x) xs

or

instance Foldable [] where
    foldMap f = go
      where
        go []     = mempty
        go (x:xs) = f x <> go xs