Foldable and ...
Foldable and Friends
Foldable
The type class Foldable generally relies on Monoid, and it describes data structures that can be folded to a summary value, so the mappend(<>) defined on Monoid is needed to show that the containing data in Foldable can be combined:
1 | class Foldable t where |
Here is a foldMap example on lists:
1 | foldMapList :: Monoid m => (a -> m) -> [a] -> m |
The minimal definition is foldMap or foldr, and using one of these two we can define them all:
1 | fold = foldMap id |
However, the folding directions are not absolute. For example, we can define foldl in terms of foldr (even though it is not efficient enough, it still shows some type of homomorphism):
1 | foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b |
To understand the entire process, we first notice that if we want to reverse the computation order, we may leave the computation “undone”, and gradually fold other elements into the process.
So in this implementation of foldl, the type b is used as the “undone” computation, i.e. function a -> a (like (+) 1), and the initial value is the identity map. After iterations, we shall get:
1 | id -- \x -> x |
When the folding process ends, we apply the “undone” computation to the initial value z, and we get the final result, in a reversed order.
In conclusion, “Folding” is the abstraction of sequential operations (or sequential recursion) on a data structure. Example:
1 | map f = foldr ((:) . f) [] -- or foldMap (pure . f) |
Traversable
Now let’s take a look at map.
1 | map :: (a -> b) -> [a] -> [b] |
We get the motivation of Traversable from the need of generalizing map to deal with effects brought by various functors.
1 | class (Functor t, Foldable t) => Traversable t where |
In the default definition of Traversable, another function sequenceA is introduced. It folds a series of effects into a single effect:
1 | sequenceA :: Applicative f => t (f a) -> f (t a) |
We can also define traverse in turn:
1 | traverse g = sequenceA . fmap g |
Foldable and ...