Sun 22 Jun 2008
Description
Catamorphisms are generalizations of Haskell's foldr. A catamorphism deconstructs a data structure with an F-algebra.
History
The name catamorphism appears to have been chosen by Lambert Meertens [1]. The category theoretic machinery behind these was resolved by Grant Malcolm [2,3], and they were popularized by Meijer, Fokkinga and Paterson [4,5]. The name comes from the Greek 'κατα-' meaning "downward or according to". A useful mnemonic is to think of a catastrophe destroying something.
Notation
A catamorphism for some F-algebra
is denoted
. When the functor F can be determined unambiguously, it is usually written
or cata
.
Implementation
cata :: Functor f => Algebra f a -> FixF f -> a cata f = f . fmap (cata f) . outF
Alternate implementations
cata f = hylo f outF cata f = para (f . fmap fst)
Duality
A catamorphism is the categorical dual of an anamorphism.
Derivation
If
is the initial
-algebra for some endofunctor
and
is an
-algebra, then there is a unique
-algebra homomorphism from
to
which we denote
.
That is to say, the following diagram commutes:
Laws
| Rule | Categorically | Haskell |
|---|---|---|
| cata-cancel | ![]() |
cata phi . InF = phi . fmap (cata phi) |
| cata-refl | ![]() |
cata InF = id |
| cata-fusion | ![]() |
f . phi = phi . fmap f => f . cata phi = cata phi |
| cata-compose | ![]() |
eps :: f :~> g => cata phi . cata (In . eps) = cata (phi . eps) |
Examples
data StrF x = Cons Char x | Nil type Str = FixF StrF instance Functor StrF where fmap f (Cons a as) = Cons a (f as) fmap f Nil = Nil length :: Str -> Int length = cata phi where phi (Cons a b) = 1 + b phi Nil = 0 data NatF a = S a | Z deriving (Eq,Show) type Nat = FixF NatF instance Functor NatF where fmap f Z = Z fmap f (S z) = S (f z) plus :: Nat -> Nat -> Nat plus n = cata phi where phi Z = n phi (S m) = s m times :: Nat -> Nat -> Nat times n = cata phi where phi Z = z phi (S m) = plus n m z :: Nat z = InF Z s :: Nat -> Nat s = InF . S
Links
[Haddock] [Source] [Field Guide]
References
[1] L. Meertens. First Steps towards the theory of Rose Trees. Draft Report, CWI, Amsterdam, 1987.
[2] G. Malcolm. PhD Thesis. University of Gronigen, 1990.
[3] G. Malcolm. Data structures and program transformation. Science of Computer Programming, 14:255--279, 1990.
[4] E. Meijer. Calculating Compilers, Ph.D thesis, Utrecht State University 1992.
[5] E. Meijer, M. Fokkinga, R. Paterson, Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire. Proceedings, 5th ACM Conference on Functional Programming Languages and Computer Architecture.





June 22nd, 2008 at 10:26 pm
[…] catamorphism† […]
June 23rd, 2008 at 2:55 am
Your definition of cata fails with a kind error on InF. If you replace InF with outF, then it typechecks, and the function has the type you give with the additional requirement that f be a functor. This is corrected in the source link, but the examples should contain the Functor instance for L.
June 23rd, 2008 at 6:57 am
Fixed. Thats what I get for switching notations mid-stream. I’d originally had that as InF^-1 then swapped back to real Haskell.
June 27th, 2008 at 11:34 am
Just to understand better, you’re using |Prelude.fmap| differently from the “normal” Haskell approach of mapping a function over the values of a container. I understanding why the definitions using |fmap| work, but I’m wondering why it is called |fmap|.
For example, I would expect the instance for |StrF| to be:
> instance Functor StrF where
> fmap f (Cons a as) = Cons (f a) (fmap f as)
> fmap f Nil = Nil
Since yours is not the typical use of |fmap|, perhaps it should be something else?
June 27th, 2008 at 11:43 am
Correction: My suggestion obviously doesn’t work, because StrF is not parametrized over the type of |a|. That’s why yours works. So, given the datatype, you _are_ using the expected use of |fmap|. I suppose it’s just strange for me to see it in the context of a fixed-point view.
June 27th, 2008 at 12:19 pm
Sean,
I’ll admit its a little tricky to get your head around this particular use of fmap. It threw me for a loop when I first saw it, too.
I think its generally more informative to think about the fixed point of a bifunctor here than it is to think about fixing a functor.
Then you can see the other definition clearly.
In category-extras that is defined through the Fix type, which takes a bifunctor, and ties its second argument. In that setting:
data ListF a x = Cons a x | Nil
type List a = Fix (ListF a)
instance Bifunctor ListF where
bimap f g (Cons a b) = Cons (f a) (g b)
and the category-extras instance that says
instance Bifunctor f => Functor (Fix f)
gives you the usual Functor for lists (modulo the noise of the explicit In constructors)
To keep the example simple, I stuck to a simple endofunctor in the example above and fixed a to a particular type yielding the String example.
July 4th, 2008 at 12:34 pm
[…] An anamorphism is the categorical dual of a catamorphism. […]
July 4th, 2008 at 12:46 pm
[…] Description A paramorphism is used when a catamorphism won’t quite do because you need not only the result of recursing with the F-algebra over the tail of a structure, but you also need the tail itself. This is another way to say that you call in a paramorphism when you need the power of primitive recursion. […]