Skip to the content.

Terminal F-Coalgebra of monoid and cofree monoid

We saw F-Algebra of monoid in this post. Then, what’s the dual of it? It’s a F-Coalgebra of monoid. Let’s see what it is.

We will use the same Monoid definition in this post.

data Monoid a = Append a a
              | Empty
  deriving Show

instance Functor Monoid where
    fmap f (Append n m) = Append (f n) (f m)
    fmap _ Empty = Empty

F-Algebra was a pair of a carrier type a and an evaluation function of type Monoid a -> a. F-Coalgebra is a pair of a carrier type a and a function of type a -> Monoid a where an arrow is reversed.

For example, we can define such coalgebra like this.

sumIntCoalg :: Int -> Monoid Int
sumIntCoalg 0 = Empty
sumIntCoalg n = let m = n `div` 2 in Append m (n - m)

This coalgebra builds a monoid by dividing a number so that their sum becomes the number. You can build a monoid like this with it.

v1, v2 :: Monoid Int
v1 = sumIntCoalg 0
v2 = sumIntCoalg 3

v1 will be Empty and v2 will be Append 1 2.

Just like we did for the F-Algebra, let’s see what happens when we use Monoid Int as a carrier type.

sumMonoidIntCoalg :: Monoid Int -> Monoid (Monoid Int)
sumMonoidIntCoalg Empty = Empty
sumMonoidIntCoalg (Append m n) = Append (sumIntCoalg m) (sumIntCoalg n)

sumMonoidIntCoalg splits each value in Append into two by applying sumIntCoalg.

v3 :: Monoid (Monoid Int)
v3 = sumMonoidIntCoalg $ sumIntCoalg 3

v3 will be Append (Append 0 1) (Append 1 1).

Let’s repeat this one more time and use Monoid (Monoid Int) as a carrier type.

sumMonoidMonoidIntCoalg :: Monoid (Monoid Int) -> Monoid (Monoid (Monoid Int))
sumMonoidMonoidIntCoalg Empty = Empty
sumMonoidMonoidIntCoalg (Append m n) = Append (sumMonoidIntCoalg m) (sumMonoidIntCoalg n)

sumMonoidMonoidIntCoalg again splits each value in Append into two.

v4 :: Monoid (Monoid (Monoid Int))
v4 = sumMonoidMonoidIntCoalg $ sumMonoidIntCoalg $ sumIntCoalg 5

v4 will be Append (Append Empty (Append 0 1)) (Append (Append 0 1) (Append 0 1)). This time, it’ll generate Empty because the first parameter of Append is 0.

When you repeat this process infinite times, you’ll get a point where repeating it one more time doesn’t change its structure. We can express it using Fix again.

newtype Fix f = In (f (Fix f))

out :: Fix f -> f (Fix f)
out (In f) = f

In the category of F-Algebra, a pair of Fix Monoid and In was an initial object. In the category of F-Coalgebra, a pair of Fix Monoid and out is a terminal object. There should be a unique morphism from any other objects to it.

Let’s take Monoid Int and sumIntCoalg as an example.

                    fmap homSumIntToTerminal
Monoid (Fix Monoid)            ←←←           Monoid Int
         ↑ ↓                                     ↑
     out ↑ ↓ In                                  ↑ sumIntCoalg
         ↑ ↓                                     ↑
     Fix Monoid                ←←←              Int
                      homSumIntToTerminal

We can define a homomorphism homSumIntToTerminal from Int to Fix Monoid by first applying sumIntCoalgo, then fmap homSumIntToTerminal, and finally In.

homSumIntToTerminal :: Int -> Fix Monoid
homSumIntToTerminal = In . fmap homSumIntToTerminal . sumIntCoalg

You can apply Int to homSumIntToTerminal to get a terminal coalgebra.

m0, m1 :: Fix Monoid
m0 = homSumIntToTerminal 0
m1 = homSumIntToTerminal 3

m0 becomes Fix Empty, but m1 becomes Append containing an infinite number of Empty and Append inside.

Just like an initial F-Algebra defines a free structure, a terminal F-Coalgebra defines a cofree structure which is a maximum structure of the monoid. For example, when you have 3, we can expand it to an infinite size of monoid like (0 <> (0 <> (... <> 1))) <> ((0 <> (... <> 1)) <> (0 <> (... <> 1))) by prepending its unit (0) to each non-unit value.

By the way, homSumIntToTerminal can be generalized to any F-Coalgebra of type Int -> Monoid Int like this.

homIntToTerminal :: (Int -> Monoid Int) -> Int -> Fix Monoid
homIntToTerminal coalg = In . fmap (homIntToTerminal coalg) . coalg

It can be further generalized to any F-Coalgebra of type a -> Monoid a.

hom :: (a -> Monoid a) -> a -> Fix Monoid
hom coalg = In . fmap (hom coalg) . coalg

Finally, it can be generalized to any functor f, and it’s called an anamorphism.

ana :: Functor f => (a -> f a) -> a -> Fix f
ana coalg = In . fmap (ana coalg) . coalg