Chapter 3.8 - F-Algebras

This commit is contained in:
Igal Tabachnik 2017-09-26 22:09:32 +03:00
parent ccc974aaa4
commit 7836fc1177
2 changed files with 163 additions and 152 deletions

View File

@ -1,12 +1,4 @@
\begin{quote}
This is part 24 of Categories for Programmers. Previously:
\href{https://bartoszmilewski.com/2017/01/02/comonads/}{Comonads}. See
the
\href{https://bartoszmilewski.com/2014/10/28/category-theory-for-programmers-the-preface/}{Table
of Contents}.
\end{quote}
We've seen several formulations of a monoid: as a set, as a
\lettrine[lhang=0.17]{W}{e've seen several} formulations of a monoid: as a set, as a
single-object category, as an object in a monoidal category. How much
more juice can we squeeze out of this simple concept?
@ -14,9 +6,9 @@ Let's try. Take this definition of a monoid as a set \code{m} with a
pair of functions:
\begin{verbatim}
μ :: m × m -> m η :: 1 -> m
μ :: m × m -> m
η :: 1 -> m
\end{verbatim}
Here, 1 is the terminal object in \textbf{Set} --- the singleton set.
The first function defines multiplication (it takes a pair of elements
and returns their product), the second selects the unit element from
@ -27,23 +19,21 @@ and just consider ``potential monoids.'' A pair of functions is an
element of a cartesian product of two sets of functions. We know that
these sets may be represented as exponential objects:
\begin{verbatim}
μ ∈ m m×m η ∈ m1
\end{verbatim}
\begin{Verbatim}[commandchars=\\\{\}]
μ \ensuremath{\in} m \textsuperscript{m×m}
η \ensuremath{\in} m\textsuperscript{1}
\end{Verbatim}
The cartesian product of these two sets is:
\begin{verbatim}
m m×m × m1
\end{verbatim}
\begin{Verbatim}[commandchars=\\\{\}]
m m×m × m\textsuperscript{1}
\end{Verbatim}
Using some high-school algebra (which works in every cartesian closed
category), we can rewrite it as:
\begin{verbatim}
m m×m + 1
\end{verbatim}
\begin{Verbatim}[commandchars=\\\{\}]
m \textsuperscript{m×m + 1}
\end{Verbatim}
The plus sign stands for the coproduct in \textbf{Set}. We have just
replaced a pair of functions with a single function --- an element of
the set:
@ -51,7 +41,6 @@ the set:
\begin{verbatim}
m × m + 1 -> m
\end{verbatim}
Any element of this set of functions is a potential monoid.
The beauty of this formulation is that it leads to interesting
@ -63,15 +52,15 @@ addition as a binary operation, zero as the unit, and negation as the
inverse. To define a group we would start with a triple of functions:
\begin{verbatim}
m × m -> m m -> m 1 -> m
m × m -> m
m -> m
1 -> m
\end{verbatim}
As before, we can combine all these triples into one set of functions:
\begin{verbatim}
m × m + m + 1 -> m
\end{verbatim}
We started with one binary operator (addition), one unary operator
(negation), and one nullary operator (identity --- here zero). We
combined them into one function. All functions with this signature
@ -105,7 +94,6 @@ object \code{a}, and a morphism
\begin{verbatim}
F a -> a
\end{verbatim}
The object is often called the carrier, an underlying object or, in the
context of programming, the carrier \newterm{type}. The morphism is often
called the evaluation function or the structure map. Think of the
@ -117,7 +105,6 @@ Here's the Haskell definition of an F-algebra:
\begin{verbatim}
type Algebra f a = f a -> a
\end{verbatim}
It identifies the algebra with its evaluation function.
In the monoid example, the functor in question is:
@ -125,27 +112,33 @@ In the monoid example, the functor in question is:
\begin{verbatim}
data MonF a = MEmpty | MAppend a a
\end{verbatim}
This is Haskell for \code{1\ +\ a\ ×\ a} (remember
\href{https://bartoszmilewski.com/2015/01/13/simple-algebraic-data-types/}{algebraic
This is Haskell for \code{1 + a × a} (remember
\hyperref[simple-algebraic-data-types]{algebraic
data structures}).
A ring would be defined using the following functor:
\begin{verbatim}
data RingF a = RZero | ROne | RAdd a a | RMul a a | RNeg a
data RingF a = RZero
| ROne
| RAdd a a
| RMul a a
| RNeg a
\end{verbatim}
which is Haskell for \code{1\ +\ 1\ +\ a\ ×\ a\ +\ a\ ×\ a\ +\ a}.
which is Haskell for \code{1 + 1 + a × a + a × a + a}.
An example of a ring is the set of integers. We can choose
\code{Integer} as the carrier type and define the evaluation function
as:
\begin{verbatim}
evalZ :: Algebra RingF Integer evalZ RZero = 0 evalZ ROne = 1 evalZ (RAdd m n) = m + n evalZ (RMul m n) = m * n evalZ (RNeg n) = -n
\end{verbatim}
\begin{Verbatim}[commandchars=\\\{\}]
evalZ :: \textbf{Algebra} RingF Integer
evalZ RZero = 0
evalZ ROne = 1
evalZ (RAdd m n) = m + n
evalZ (RMul m n) = m * n
evalZ (RNeg n) = -n
\end{Verbatim}
There are more F-algebras based on the same functor \code{RingF}. For
instance, polynomials form a ring and so do square matrices.
@ -162,15 +155,22 @@ instance, an arbitrary expression in a ring is generated by this
tree-like data structure:
\begin{verbatim}
data Expr = RZero | ROne | RAdd Expr Expr | RMul Expr Expr | RNeg Expr
data Expr = RZero
| ROne
| RAdd Expr Expr
| RMul Expr Expr
| RNeg Expr
\end{verbatim}
We can replace the original ring evaluator with its recursive version:
\begin{verbatim}
evalZ :: Expr -> Integer evalZ RZero = 0 evalZ ROne = 1 evalZ (RAdd e1 e2) = evalZ e1 + evalZ e2 evalZ (RMul e1 e2) = evalZ e1 * evalZ e2 evalZ (RNeg e) = -(evalZ e)
evalZ :: Expr -> Integer
evalZ RZero = 0
evalZ ROne = 1
evalZ (RAdd e1 e2) = evalZ e1 + evalZ e2
evalZ (RMul e1 e2) = evalZ e1 * evalZ e2
evalZ (RNeg e) = -(evalZ e)
\end{verbatim}
This is still not very practical, since we are forced to represent all
integers as sums of ones, but it will do in a pinch.
@ -183,7 +183,6 @@ define a depth-one tree as:
\begin{verbatim}
type RingF1 a = RingF (RingF a)
\end{verbatim}
We are filling the holes in the definition of \code{RingF} with
depth-zero trees generated by \code{RingF\ a}. Depth-2 trees are
similarly obtained as:
@ -191,19 +190,16 @@ similarly obtained as:
\begin{verbatim}
type RingF2 a = RingF (RingF (RingF a))
\end{verbatim}
which we can also write as:
\begin{verbatim}
type RingF2 a = RingF (RingF1 a)
\end{verbatim}
Continuing this process, we can write a symbolic equation:
\begin{verbatim}
type RingFn+1 a = RingF (RingFn a)
\end{verbatim}
\begin{Verbatim}[commandchars=\\\{\}]
type RingF\textsubscript{n+1} a = RingF (RingF\textsubscript{n} a)
\end{Verbatim}
Conceptually, after repeating this process infinitely many times, we end
up with our \code{Expr}. Notice that \code{Expr} does not depend on
\code{a}. The starting point of our journey doesn't matter, we always
@ -220,37 +216,33 @@ point}, an object defined as:
\begin{verbatim}
Fix f = f (Fix f)
\end{verbatim}
The intuition behind this definition is that, since we applied
\code{f} infinitely many times to get \code{Fix\ f}, applying it one
\code{f} infinitely many times to get \code{Fix f}, applying it one
more time doesn't change anything. In Haskell, the definition of a fixed
point is:
\begin{verbatim}
newtype Fix f = Fix (f (Fix f))
\end{verbatim}
Arguably, this would be more readable if the constructor's name were
different than the name of the type being defined, as in:
\begin{verbatim}
newtype Fix f = In (f (Fix f))
\end{verbatim}
but I'll stick with the accepted notation. The constructor \code{Fix}
(or \code{In}, if you prefer) can be seen as a function:
\begin{verbatim}
Fix :: f (Fix f) -> Fix f
\end{verbatim}
There is also a function that peels off one level of functor
application:
\begin{verbatim}
unFix :: Fix f -> f (Fix f) unFix (Fix x) = x
unFix :: Fix f -> f (Fix f)
unFix (Fix x) = x
\end{verbatim}
The two functions are the inverse of each other. We'll use these
functions later.
@ -261,12 +253,12 @@ constructing some new objects, see if they form a category. Not
surprisingly, algebras over a given endofunctor \code{F} form a
category. Objects in that category are algebras --- pairs consisting of
a carrier object \code{a} and a morphism
\code{F\ a\ ->\ a}, both from the original category
\code{F a -> a}, both from the original category
\emph{C}.
To complete the picture, we have to define morphisms in the category of
F-algebras. A morphism must map one algebra \code{(a,\ f)} to another
algebra \code{(b,\ g)}. We'll define it as a morphism \code{m} that
F-algebras. A morphism must map one algebra \code{(a, f)} to another
algebra \code{(b, g)}. We'll define it as a morphism \code{m} that
maps the carriers --- it goes from \code{a} to \code{b} in the
original category. Not any morphism will do: we want it to be compatible
with the two evaluators. (We call such a structure-preserving morphism a
@ -276,18 +268,21 @@ F-algebras. First, notice that we can lift \code{m} to the mapping:
\begin{verbatim}
F m :: F a -> F b
\end{verbatim}
we can then follow it with \code{g} to get to \code{b}.
Equivalently, we can use \code{f} to go from \code{F\ a} to
Equivalently, we can use \code{f} to go from \code{F a} to
\code{a} and then follow it with \code{m}. We want the two paths to
be equal:
\begin{verbatim}
g ∘ F m = m ∘ f
g ◦ F m = m ◦ f
\end{verbatim}
\includegraphics[width=2.09375in]{images/alg.png}
\begin{figure}[H]
\centering
\includegraphics[width=50mm]{images/alg.png}
\end{figure}
\noindent
It's easy to convince yourself that this is indeed a category (hint:
identity morphisms from \emph{C} work just fine, and a composition of
homomorphisms is a homomorphism).
@ -295,108 +290,124 @@ homomorphisms is a homomorphism).
An initial object in the category of F-algebras, if it exists, is called
the \newterm{initial algebra}. Let's call the carrier of this initial
algebra \code{i} and its evaluator
\code{j\ ::\ F\ i\ ->\ i}. It turns out that \code{j},
\code{j :: F i -> i}. It turns out that \code{j},
the evaluator of the initial algebra, is an isomorphism. This result is
known as Lambek's theorem. The proof relies on the definition of the
initial object, which requires that there be a unique homomorphism
\code{m} from it to any other F-algebra. Since \code{m} is a
homomorphism, the following diagram must commute:
\includegraphics{images/alg2.png}
\begin{figure}[H]
\centering
\includegraphics[width=50mm]{images/alg2.png}
\end{figure}
Now let's construct an algebra whose carrier is \code{F\ i}. The
evaluator of such an algebra must be a morphism from \code{F\ (F\ i)}
to \code{F\ i}. We can easily construct such an evaluator simply by
\noindent
Now let's construct an algebra whose carrier is \code{F i}. The
evaluator of such an algebra must be a morphism from \code{F (F i)}
to \code{F i}. We can easily construct such an evaluator simply by
lifting \code{j}:
\begin{verbatim}
F j :: F (F i) -> F i
\end{verbatim}
Because \code{(i,\ j)} is the initial algebra, there must be a unique
homomorphism \code{m} from it to \code{(F\ i,\ F\ j)}. The following
Because \code{(i, j)} is the initial algebra, there must be a unique
homomorphism \code{m} from it to \code{(F i, F j)}. The following
diagram must commute:
\includegraphics{images/alg3a.png}
\begin{figure}[H]
\centering
\includegraphics[width=50mm]{images/alg3a.png}
\end{figure}
\noindent
But we also have this trivially commuting diagram (both paths are the
same!):
\includegraphics{images/alg3.png}
\begin{figure}[H]
\centering
\includegraphics[width=50mm]{images/alg3.png}
\end{figure}
\noindent
which can be interpreted as showing that \code{j} is a homomorphism of
algebras, mapping \code{(F\ i,\ F\ j)} to \code{(i,\ j)}. We can
algebras, mapping \code{(F i, F j)} to \code{(i, j)}. We can
glue these two diagrams together to get:
\includegraphics[width=3.12500in]{images/alg4.png}
\begin{figure}[H]
\centering
\includegraphics[width=80mm]{images/alg4.png}
\end{figure}
\noindent
This diagram may, in turn, be interpreted as showing that
\code{j\ \ m} is a homomorphism of algebras. Only in this case the
two algebras are the same. Moreover, because \code{(i,\ j)} is
\code{jm} is a homomorphism of algebras. Only in this case the
two algebras are the same. Moreover, because \code{(i, j)} is
initial, there can only be one homomorphism from it to itself, and
that's the identity morphism \code{idi} --- which we know is a
homomorphism of algebras. Therefore \code{j\ \ m\ =\ idi}. Using this
that's the identity morphism \code{id\textsubscript{i}} --- which we know is a
homomorphism of algebras. Therefore \code{j ◦ m = id\textsubscript{i}}. Using this
fact and the commuting property of the left diagram we can prove that
\code{m\ \ j\ =\ idFi}. This shows that \code{m} is the inverse of
\code{m ◦ j = id\textsubscript{Fi}}. This shows that \code{m} is the inverse of
\code{j} and therefore \code{j} is an isomorphism between
\code{F\ i} and \code{i}:
\begin{verbatim}
F i ≅ i
\end{verbatim}
\code{F i} and \code{i}:
\begin{Verbatim}[commandchars=\\\{\}]
F i \ensuremath{\cong} i
\end{Verbatim}
But that is just saying that \code{i} is a fixed point of \code{F}.
That's the formal proof behind the original hand-waving argument.
Back to Haskell: We recognize \code{i} as our \code{Fix\ f},
Back to Haskell: We recognize \code{i} as our \code{Fix f},
\code{j} as our constructor \code{Fix}, and its inverse as
\code{unFix}. The isomorphism in Lambek's theorem tells us that, in
order to get the initial algebra, we take the functor \code{f} and
replace its argument \code{a} with \code{Fix\ f}. We also see why
replace its argument \code{a} with \code{Fix f}. We also see why
the fixed point does not depend on \code{a}.
\subsection{Natural Numbers}\label{natural-numbers}
\section{Natural Numbers}\label{natural-numbers}
Natural numbers can also be defined as an F-algebra. The starting point
is the pair of morphisms:
\begin{verbatim}
zero :: 1 -> N succ :: N -> N
zero :: 1 -> N
succ :: N -> N
\end{verbatim}
The first one picks the zero, and the second one maps all numbers to
their successors. As before, we can combine the two into one:
\begin{verbatim}
1 + N -> N
\end{verbatim}
The left hand side defines a functor which, in Haskell, can be written
like this:
\begin{verbatim}
data NatF a = ZeroF | SuccF a
\end{verbatim}
The fixed point of this functor (the initial algebra that it generates)
can be encoded in Haskell as:
\begin{verbatim}
data Nat = Zero | Succ Nat
\end{verbatim}
A natural number is either zero or a successor of another number. This
is known as the Peano representation for natural numbers.
\section{Catamorphisms}\label{catamorphisms}
Let's rewrite the initiality condition using Haskell notation. We call
the initial algebra \code{Fix\ f}. Its evaluator is the contructor
the initial algebra \code{Fix f}. Its evaluator is the contructor
\code{Fix}. There is a unique morphism \code{m} from the initial
algebra to any other algebra over the same functor. Let's pick an
algebra whose carrier is \code{a} and the evaluator is \code{alg}.
\includegraphics{images/alg5.png}\\
\begin{figure}[H]
\centering
\includegraphics[width=60mm]{images/alg5.png}
\end{figure}
\noindent
By the way, notice what \code{m} is: It's an evaluator for the fixed
point, an evaluator for the whole recursive expression tree. Let's find
a general way of implementing it.
@ -405,18 +416,21 @@ Lambek's theorem tells us that the constructor \code{Fix} is an
isomorphism. We called its inverse \code{unFix}. We can therefore flip
one arrow in this diagram to get:
\includegraphics{images/alg6.png}
\begin{figure}[H]
\centering
\includegraphics[width=60mm]{images/alg6.png}
\end{figure}
\noindent
Let's write down the commutation condition for this diagram:
\begin{verbatim}
m = alg . fmap m . unFix
\end{verbatim}
We can interpret this equation as a recursive definition of \code{m}.
The recursion is bound to terminate for any finite tree created using
the functor \code{f}. We can see that by noticing that
\code{fmap\ m} operates underneath the top layer of the functor
\code{fmap m} operates underneath the top layer of the functor
\code{f}. In other words, it works on the children of the original
tree. The children are always one level shallower than the original
tree.
@ -435,25 +449,25 @@ gives us the function we called \code{m}. This higher order function
is called a catamorphism:
\begin{verbatim}
cata :: Functor f => (f a -> a) -> Fix f -> a cata alg = alg . fmap (cata alg) . unFix
cata :: Functor f => (f a -> a) -> Fix f -> a
cata alg = alg . fmap (cata alg) . unFix
\end{verbatim}
Let's see an example of that. Take the functor that defines natural
numbers:
\begin{verbatim}
data NatF a = ZeroF | SuccF a
\end{verbatim}
Let's pick \code{(Int,\ Int)} as the carrier type and define our
Let's pick \code{(Int, Int)} as the carrier type and define our
algebra as:
\begin{verbatim}
fib :: NatF (Int, Int) -> (Int, Int) fib ZeroF = (1, 1) fib (SuccF (m, n)) = (n, m + n)
fib :: NatF (Int, Int) -> (Int, Int)
fib ZeroF = (1, 1)
fib (SuccF (m, n)) = (n, m + n)
\end{verbatim}
You can easily convince yourself that the catamorphism for this algebra,
\code{cata\ fib}, calculates Fibonacci numbers.
\code{cata fib}, calculates Fibonacci numbers.
In general, an algebra for \code{NatF} defines a recurrence relation:
the value of the current element in terms of the previous element. A
@ -466,14 +480,12 @@ A list of \code{e} is the initial algebra of the following functor:
\begin{verbatim}
data ListF e a = NilF | ConsF e a
\end{verbatim}
Indeed, replacing the variable \code{a} with the result of recursion,
which we'll call \code{List\ e}, we get:
which we'll call \code{List e}, we get:
\begin{verbatim}
data List e = Nil | Cons e (List e)
\end{verbatim}
An algebra for a list functor picks a particular carrier type and
defines a function that does pattern matching on the two constructors.
Its value for \code{NilF} tells us how to evaluate an empty list, and
@ -484,10 +496,11 @@ For instance, here's an algebra that can be used to calculate the length
of a list (the carrier type is \code{Int}):
\begin{verbatim}
lenAlg :: ListF e Int -> Int lenAlg (ConsF e n) = n + 1 lenAlg NilF = 0
lenAlg :: ListF e Int -> Int
lenAlg (ConsF e n) = n + 1
lenAlg NilF = 0
\end{verbatim}
Indeed, the resulting catamorphism \code{cata\ lenAlg} calculates the
Indeed, the resulting catamorphism \code{cata lenAlg} calculates the
length of a list. Notice that the evaluator is a combination of (1) a
function that takes a list element and an accumulator and returns a new
accumulator, and (2) a starting value, here zero. The type of the value
@ -498,22 +511,21 @@ Compare this to the traditional Haskell definition:
\begin{verbatim}
length = foldr (\e n -> n + 1) 0
\end{verbatim}
The two arguments to \code{foldr} are exactly the two components of
the algebra.
Let's try another example:
\begin{verbatim}
sumAlg :: ListF Double Double -> Double sumAlg (ConsF e s) = e + s sumAlg NilF = 0.0
sumAlg :: ListF Double Double -> Double
sumAlg (ConsF e s) = e + s
sumAlg NilF = 0.0
\end{verbatim}
Again, compare this with:
\begin{verbatim}
sum = foldr (\e s -> e + s) 0.0
\end{verbatim}
As you can see, \code{foldr} is just a convenient specialization of a
catamorphism to lists.
@ -525,23 +537,25 @@ direction of the morphism is reversed:
\begin{verbatim}
a -> F a
\end{verbatim}
Coalgebras for a given functor also form a category, with homomorphisms
preserving the coalgebraic structure. The terminal object
\code{(t,\ u)} in that category is called the terminal (or final)
coalgebra. For every other algebra \code{(a,\ f)} there is a unique
\code{(t, u)} in that category is called the terminal (or final)
coalgebra. For every other algebra \code{(a, f)} there is a unique
homomorphism \code{m} that makes the following diagram commute:
\includegraphics{images/alg7.png}
\begin{figure}[H]
\centering
\includegraphics[width=50mm]{images/alg7.png}
\end{figure}
\noindent
A terminal colagebra is a fixed point of the functor, in the sense that
the morphism \code{u\ ::\ t\ ->\ F\ t} is an isomorphism
the morphism \code{u :: t -> F t} is an isomorphism
(Lambek's theorem for coalgebras):
\begin{verbatim}
F t ≅ t
\end{verbatim}
\begin{Verbatim}[commandchars=\\\{\}]
F t \ensuremath{\cong} t
\end{Verbatim}
A terminal coalgebra is usually interpreted in programming as a recipe
for generating (possibly infinite) data structures or transition
systems.
@ -550,24 +564,23 @@ Just like a catamorphism can be used to evaluate an initial algebra, an
anamorphism can be used to coevaluate a terminal coalgebra:
\begin{verbatim}
ana :: Functor f => (a -> f a) -> a -> Fix f ana coalg = Fix . fmap (ana coalg) . coalg
ana :: Functor f => (a -> f a) -> a -> Fix f
ana coalg = Fix . fmap (ana coalg) . coalg
\end{verbatim}
A canonical example of a coalgebra is based on a functor whose fixed
point is an infinite stream of elements of type \code{e}. This is the
functor:
\begin{verbatim}
data StreamF e a = StreamF e a deriving Functor
data StreamF e a = StreamF e a
deriving Functor
\end{verbatim}
and this is its fixed point:
\begin{verbatim}
data Stream e = Stream e (Stream e)
\end{verbatim}
A coalgebra for \code{StreamF\ e} is a function that takes the seed of
A coalgebra for \code{StreamF e} is a function that takes the seed of
type \code{a} and produces a pair (\code{StreamF} is a fancy name
for a pair) consisting of an element and the next seed.
@ -584,24 +597,26 @@ the sieve of Eratosthenes. This coalgebra is implemented by the
following function:
\begin{verbatim}
era :: [Int] -> StreamF Int [Int] era (p : ns) = StreamF p (filter (notdiv p) ns) where notdiv p n = n `mod` p /= 0
era :: [Int] -> StreamF Int [Int]
era (p : ns) = StreamF p (filter (notdiv p) ns)
where notdiv p n = n `mod` p /= 0
\end{verbatim}
The anamorphism for this coalgebra generates the list of primes:
\begin{verbatim}
primes = ana era [2..]
\end{verbatim}
A stream is an infinite list, so it should be possible to convert it to
a Haskell list. To do that, we can use the same functor \code{StreamF}
to form an algebra, and we can run a catamorphism over it. For instance,
this is a catamorphism that converts a stream to a list:
\begin{verbatim}
toListC :: Fix (StreamF e) -> [e] toListC = cata al where al :: StreamF e [e] -> [e] al (StreamF e a) = e : a
toListC :: Fix (StreamF e) -> [e]
toListC = cata al
where al :: StreamF e [e] -> [e]
al (StreamF e a) = e : a
\end{verbatim}
Here, the same fixed point is simultaneously an initial algebra and a
terminal coalgebra for the same endofunctor. It's not always like this,
in an arbitrary category. In general, an endofunctor may have many (or
@ -616,16 +631,15 @@ functor is modified to produce a \code{Maybe} pair:
\begin{verbatim}
unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
\end{verbatim}
The value of \code{Nothing} will terminate the generation of the list.
An interesting case of a coalgebra is related to lenses. A lens can be
represented as a pair of a getter and a setter:
\begin{verbatim}
set :: a -> s -> a get :: a -> s
set :: a -> s -> a
get :: a -> s
\end{verbatim}
Here, \code{a} is usually some product data type with a field of type
\code{s}. The getter retrieves the value of that field and the setter
replaces this field with a new value. These two functions can be
@ -634,24 +648,21 @@ combined into one:
\begin{verbatim}
a -> (s, s -> a)
\end{verbatim}
We can rewrite this function further as:
\begin{verbatim}
a -> Store s a
\end{verbatim}
where we have defined a functor:
\begin{verbatim}
data Store s a = Store (s -> a) s
\end{verbatim}
Notice that this is not a simple algebraic functor constructed from sums
of products. It involves an exponential \code{as}.
A lens is a coalgebra for this functor with the carrier type \code{a}.
We've seen before that \code{Store\ s} is also a comonad. It turns out
We've seen before that \code{Store s} is also a comonad. It turns out
that a well-behaved lens corresponds to a coalgebra that is compatible
with the comonad structure. We'll talk about this in the next section.
@ -662,12 +673,12 @@ with the comonad structure. We'll talk about this in the next section.
\item
Implement the evaluation function for a ring of polynomials of one
variable. You can represent a polynomial as a list of coefficients in
front of powers of \code{x}. For instance, \code{4x2-1} would be
front of powers of \code{x}. For instance, \code{4x\textsuperscript{2}-1} would be
represented as (starting with the zero'th power)
\code{{[}-1,\ 0,\ 4{]}}.
\code{{[}-1, 0, 4{]}}.
\item
Generalize the previous construction to polynomials of many
independent variables, like \code{x2y-3y3z}.
independent variables, like \code{x\textsuperscript{2}y-3y\textsuperscript{3}z}.
\item
Implement the algebra for the ring of 2×2 matrices.
\item
@ -676,8 +687,4 @@ with the comonad structure. We'll talk about this in the next section.
\item
Use \code{unfoldr} to generate a list of the first \code{n}
primes.
\end{enumerate}
Next:
\href{https://bartoszmilewski.com/2017/03/14/algebras-for-monads/}{Algebras
for Monads}.
\end{enumerate}

View File

@ -95,7 +95,7 @@ PDF compiled by @url{https://github.com/hmemcpy/milewski-ctfp-pdf, Igal Tabachni
\chapter{Products and Coproducts}\label{products-and-coproducts}
\subfile{content/1.5/Products and Coproducts}
\chapter{Simple Algebraic Data Types}
\chapter{Simple Algebraic Data Types}\label{simple-algebraic-data-types}
\subfile{content/1.6/Simple Algebraic Data Types}
\chapter{Functors}
@ -135,6 +135,10 @@ PDF compiled by @url{https://github.com/hmemcpy/milewski-ctfp-pdf, Igal Tabachni
\chapter{Comonads}\label{comonads}
\subfile{content/3.7/Comonads}
\chapter{F-Algebras}\label{f-algebras}
\subfile{content/3.8/F-Algebras}
\backmatter
@unnumbered Acknowledgments