In functional programming, the concept of catamorphism (from the Ancient Greek: "downwards" and "form, shape") denotes the unique homomorphism from an initial algebra into some other algebra.
Catamorphisms provide generalizations of folds of lists to arbitrary algebraic data types, which can be described as initial algebras. The dual concept is that of anamorphism that generalize unfolds. A hylomorphism is the composition of an anamorphism followed by a catamorphism.
(A,in)
F
in
FA
A
(X,f)
F
f
FX
X
h
(A,in)
(X,f)
F
h
A
X
h
h\circin=f\circFh
F
cata f
h=cata f
h\circin=f\circFh
Another notation found in the literature is
(|f|)
We give a series of examples, and then a more global approach to catamorphisms, in the Haskell programming language.
Consider the functor Maybe
defined in the below Haskell code:
class Functor f where -- class for functors fmap :: (a -> b) -> (f a -> f b) -- action of functor on morphisms
instance Functor Maybe where -- turn Maybe into a functor fmap g Nothing = Nothing fmap g (Just x) = Just (g x)
The initial object of the Maybe-Algebra is the set of all objects of natural number type Nat
together with the morphism ini
defined below:[3] [4]
ini :: Maybe Nat -> Nat -- initial object of Maybe-algebra (with slight abuse of notation) ini Nothing = Zeroini (Just n) = Succ nThe cata
map can be defined as follows:[4] cata g ((Succ. Succ . Succ) Zero)
will evaluate to "wait... wait... wait... go!".
For a fixed type a
consider the functor MaybeProd a
defined by the following:
class Functor f where -- class for functors fmap :: (a -> b) -> (f a -> f b) -- action of functor on morphisms
instance Functor (MaybeProd a) where -- turn MaybeProd a into a functor, the functoriality is only in the second type variable fmap g Nothing = Nothing fmap g (Just (x,y)) = Just (x, g y)The initial algebra of MaybeProd a
is given by the lists of elements with type a
together with the morphism ini
defined below:[5]
ini :: MaybeProd a (List a) -> List a -- initial algebra of MaybeProd aini Nothing = EmptyListini (Just (n,l)) = Cons n lThe cata
map can be defined by:cata g (Cons s l) = g (Just (s, cata g l))
.As an example consider the following morphism:cata g (Cons 10 EmptyList)
evaluates to 30. This can be seen by expandingcata g (Cons 10 EmptyList)=g (Just (10,cata g EmptyList)) = 10* cata g EmptyList=10* g Nothing=10*3
.
In the same way it can be shown, thatcata g (Cons 10 (Cons 100 (Cons 1000 EmptyList)))
will evaluate to 10*(100*(1000*3))=3.000.000.
The cata
map is closely related to the right fold (see Fold (higher-order function)) of lists foldrList
.The morphism lift
defined bycata
to the right fold foldrList
of lists via:cata
implies, that foldrList
is the right fold and not the left fold.As an example: foldrList (+) 1 (Cons 10 (Cons 100 (Cons 1000 EmptyList)))
will evaluate to 1111 and foldrList (*) 3 (Cons 10 (Cons 100 (Cons 1000 EmptyList)))
to 3.000.000.
For a fixed type a
, consider the functor mapping types b
to a type that contains a copy of each term of a
as well as all pairs of b
's (terms of the product type of two instances of the type b
). An algebra consists of a function to b
, which either acts on an a
term or two b
terms. This merging of a pair can be encoded as two functions of type a -> b
resp. b -> b -> b
.
Deeper category theoretical studies of initial algebras reveal that the F-algebra obtained from applying the functor to its own initial algebra is isomorphic to it.
Strong type systems enable us to abstractly specify the initial algebra of a functor f
as its fixed point a = f a. The recursively defined catamorphisms can now be coded in single line, where the case analysis (like in the different examples above) is encapsulated by the fmap. Since the domain of the latter are objects in the image of f
, the evaluation of the catamorphisms jumps back and forth between a
and f a
.
newtype Fix f = Iso -- gives us the initial algebra for the functor f
cata :: Functor f => Algebra f a -> (Fix f -> a) -- catamorphism from Fix f to acata alg = alg . fmap (cata alg) . invIso -- note that invIso and alg map in opposite directions
Now again the first example, but now via passing the Maybe functor to Fix. Repeated application of the Maybe functor generates a chain of types, which, however, can be united by the isomorphism from the fixed point theorem. We introduce the term zero
, which arises from Maybe's Nothing
and identify a successor function with repeated application of the Just
. This way the natural numbers arise.
Again, the following will evaluate to "wait.. wait.. wait.. wait.. go!": cata pleaseWait (successor.successor.successor.successor $ zero)
And now again the tree example. For this we must provide the tree container data type so that we can set up the fmap
(we didn't have to do it for the Maybe
functor, as it's part of the standard prelude).
The following will evaluate to 4: cata treeDepth $ meet (end "X") (meet (meet (end "YXX") (end "YXY")) (end "YY"))