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 ...