In category theory, the concept of catamorphism (from Greek: κατά = downwards or according to; μορφή = form or shape) denotes the unique homomorphism from an initial algebra into some other algebra.
Contents
In functional programming, 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.
Definition
Consider an initial F-algebra (A, in) for some endofunctor F of some category into itself. Here in is a morphism from FA to A. Since it is initial, we know that whenever (X, f) is another F-algebra, i.e. a morphism f from FX to X, there is a unique homomorphism h from (A, in) to (X, f). By the definition of the category of F-algebras, this h corresponds to a morphism from A to X, conventionally also denoted h, such that
Terminology and history
Another notation found in the literature is
Examples
We give a series of examples, and then a more global approach to catamorphisms, in the Haskell programming language.
Iteration
Iteration-step prescriptions lead to natural numbers as initial object.
Consider the functor fmaybe
mapping a data type b
to a data type fmaybe b
, which contains a copy of each term from b
as well as one additional term Nothing
(in Haskell, this is what Maybe
does). This can be encoded using one term and one function. So let an instance of a StepAlgebra also include a function from fmaybe b
to b
, which maps Nothing
to a fixed term nil
of b
, and where the actions on the copied terms will be called next
.
As a silly example, consider the algebra on strings encoded as ("go!", s -> "wait.. " ++ s)
, for which Nothing
is mapped to "go!"
and otherwise "wait.. "
is prepended. As (Succ . Succ . Succ . Succ $ Zero)
denotes the number four in Nat
, the following will evaluate to "wait.. wait.. wait.. wait.. go!": foldSteps ("go!", s -> "wait.. " ++ s) (Succ . Succ . Succ . Succ $ Zero)
. We can easily change the code to a more useful operation, say repeated operation of an algebraic operation on numbers, just by changing the F-algebra (nil, next)
, which is passed to foldSteps
List fold
For a fixed type a
, consider the functor mapping types b
to the product type of those two types. We moreover also add a term Nil
to this resulting type. An f-algebra shall now map Nil
to some special term nil
of b
or "merge" a pair (any other term of the constructed type) into a term of b
. This merging of a a pair can be encoded as a function of type a -> b -> b
.
As an example, consider the algebra on numbers types encoded as (3, x-> y-> x*y)
, for which the number from a
acts on the number from b
by plain multiplication. Then the following will evaluate to 3.000.000: foldrList (3, x-> y-> x*y) (Cons 10 $ Cons 100 $ Cons 1000 Nil)
Tree fold
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
.
General case
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
.
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 Maybes'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"))