mirror of
https://github.com/hmemcpy/milewski-ctfp-pdf.git
synced 2024-11-22 20:01:25 +03:00
More style tweaks for chapters 1.3 and 1.4
This commit is contained in:
parent
2662dd8b6e
commit
a0bd6ef215
@ -90,7 +90,6 @@ Associativity means that:
|
|||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
(a + b) + c = a + (b + c)
|
(a + b) + c = a + (b + c)
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
(In other words, we can skip parentheses when adding numbers.)
|
(In other words, we can skip parentheses when adding numbers.)
|
||||||
|
|
||||||
The neutral element is zero, because:
|
The neutral element is zero, because:
|
||||||
@ -98,13 +97,11 @@ The neutral element is zero, because:
|
|||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
0 + a = a
|
0 + a = a
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
and
|
and
|
||||||
|
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
a + 0 = a
|
a + 0 = a
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
The second equation is redundant, because addition is commutative \code{(a + b
|
The second equation is redundant, because addition is commutative \code{(a + b
|
||||||
= b + a)}, but commutativity is not part of the definition of a monoid.
|
= b + a)}, but commutativity is not part of the definition of a monoid.
|
||||||
For instance, string concatenation is not commutative and yet it forms a
|
For instance, string concatenation is not commutative and yet it forms a
|
||||||
@ -116,12 +113,11 @@ In Haskell we can define a type class for monoids --- a type for which
|
|||||||
there is a neutral element called \code{mempty} and a binary operation
|
there is a neutral element called \code{mempty} and a binary operation
|
||||||
called \code{mappend}:
|
called \code{mappend}:
|
||||||
|
|
||||||
\begin{minted}{haskell}
|
\begin{verbatim}
|
||||||
class Monoid m where
|
class Monoid m where
|
||||||
mempty :: m
|
mempty :: m
|
||||||
mappend :: m -> m -> m
|
mappend :: m -> m -> m
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
The type signature for a two-argument function,
|
The type signature for a two-argument function,
|
||||||
\code{m->m->m}, might look strange at first,
|
\code{m->m->m}, might look strange at first,
|
||||||
but it will make perfect sense after we talk about currying. You may
|
but it will make perfect sense after we talk about currying. You may
|
||||||
@ -147,12 +143,11 @@ be a monoid by providing the implementation of \code{mempty} and
|
|||||||
\code{mappend} (this is, in fact, done for you in the standard
|
\code{mappend} (this is, in fact, done for you in the standard
|
||||||
Prelude):
|
Prelude):
|
||||||
|
|
||||||
\begin{minted}{haskell}
|
\begin{verbatim}
|
||||||
instance Monoid String where
|
instance Monoid String where
|
||||||
mempty = ""
|
mempty = ""
|
||||||
mappend = (++)
|
mappend = (++)
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
Here, we have reused the list concatenation operator \code{(++)},
|
Here, we have reused the list concatenation operator \code{(++)},
|
||||||
because a \code{String} is just a list of characters.
|
because a \code{String} is just a list of characters.
|
||||||
|
|
||||||
@ -160,16 +155,14 @@ A word about Haskell syntax: Any infix operator can be turned into a
|
|||||||
two-argument function by surrounding it with parentheses. Given two
|
two-argument function by surrounding it with parentheses. Given two
|
||||||
strings, you can concatenate them by inserting \code{++} between them:
|
strings, you can concatenate them by inserting \code{++} between them:
|
||||||
|
|
||||||
\begin{minted}{haskell}
|
\begin{verbatim}
|
||||||
"Hello " ++ "world!"
|
"Hello " ++ "world!"
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
or by passing them as two arguments to the parenthesized \code{(++)}:
|
or by passing them as two arguments to the parenthesized \code{(++)}:
|
||||||
|
|
||||||
\begin{minted}{haskell}
|
\begin{verbatim}
|
||||||
(++) "Hello " "world!"
|
(++) "Hello " "world!"
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
Notice that arguments to a function are not separated by commas or
|
Notice that arguments to a function are not separated by commas or
|
||||||
surrounded by parentheses. (This is probably the hardest thing to get
|
surrounded by parentheses. (This is probably the hardest thing to get
|
||||||
used to when learning Haskell.)
|
used to when learning Haskell.)
|
||||||
@ -177,17 +170,15 @@ used to when learning Haskell.)
|
|||||||
It's worth emphasizing that Haskell lets you express equality of
|
It's worth emphasizing that Haskell lets you express equality of
|
||||||
functions, as in:
|
functions, as in:
|
||||||
|
|
||||||
\begin{minted}{haskell}
|
\begin{verbatim}
|
||||||
mappend = (++)
|
mappend = (++)
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
Conceptually, this is different than expressing the equality of values
|
Conceptually, this is different than expressing the equality of values
|
||||||
produced by functions, as in:
|
produced by functions, as in:
|
||||||
|
|
||||||
\begin{minted}{haskell}
|
\begin{verbatim}
|
||||||
mappend s1 s2 = (++) s1 s2
|
mappend s1 s2 = (++) s1 s2
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
The former translates into equality of morphisms in the category
|
The former translates into equality of morphisms in the category
|
||||||
\textbf{Hask} (or \textbf{Set}, if we ignore bottoms, which is the name
|
\textbf{Hask} (or \textbf{Set}, if we ignore bottoms, which is the name
|
||||||
for never-ending calculations). Such equations are not only more
|
for never-ending calculations). Such equations are not only more
|
||||||
@ -204,7 +195,7 @@ so this might be a little confusing to the beginner.)
|
|||||||
The closest one can get to declaring a monoid in C++ would be to use the
|
The closest one can get to declaring a monoid in C++ would be to use the
|
||||||
(proposed) syntax for concepts.
|
(proposed) syntax for concepts.
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
template<class T>
|
template<class T>
|
||||||
T mempty = delete;
|
T mempty = delete;
|
||||||
|
|
||||||
@ -216,9 +207,7 @@ template<class M>
|
|||||||
{ mempty<M> } -> M;
|
{ mempty<M> } -> M;
|
||||||
{ mappend(m, m); } -> M;
|
{ mappend(m, m); } -> M;
|
||||||
};
|
};
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
|
|
||||||
The first definition uses a value template (also proposed). A
|
The first definition uses a value template (also proposed). A
|
||||||
polymorphic value is a family of values --- a different value for every
|
polymorphic value is a family of values --- a different value for every
|
||||||
type.
|
type.
|
||||||
@ -234,14 +223,14 @@ type) that tests whether there exist appropriate definitions of
|
|||||||
An instantiation of the Monoid concept can be accomplished by providing
|
An instantiation of the Monoid concept can be accomplished by providing
|
||||||
appropriate specializations and overloads:
|
appropriate specializations and overloads:
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
template<>
|
template<>
|
||||||
std::string mempty<std::string> = {""};
|
std::string mempty<std::string> = {""};
|
||||||
|
|
||||||
std::string mappend(std::string s1, std::string s2) {
|
std::string mappend(std::string s1, std::string s2) {
|
||||||
return s1 + s2;
|
return s1 + s2;
|
||||||
}
|
}
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
\section{Monoid as Category}\label{monoid-as-category}
|
\section{Monoid as Category}\label{monoid-as-category}
|
||||||
|
|
||||||
@ -287,8 +276,8 @@ morphisms that follow appropriate rules of composition.
|
|||||||
|
|
||||||
|
|
||||||
\begin{figure}
|
\begin{figure}
|
||||||
\centering
|
\centering
|
||||||
\fbox{\includegraphics[width=2.45833in]{images/monoid.jpg}}
|
\fbox{\includegraphics[width=2.45833in]{images/monoid.jpg}}
|
||||||
\end{figure}
|
\end{figure}
|
||||||
|
|
||||||
String concatenation is an interesting case, because we have a choice of
|
String concatenation is an interesting case, because we have a choice of
|
||||||
@ -315,10 +304,10 @@ this product. So we can always recover a set monoid from a category
|
|||||||
monoid. For all intents and purposes they are one and the same.
|
monoid. For all intents and purposes they are one and the same.
|
||||||
|
|
||||||
\begin{figure}
|
\begin{figure}
|
||||||
\centering
|
\centering
|
||||||
\includegraphics[width=3.12500in]{images/monoidhomset.jpg}
|
\includegraphics[width=3.12500in]{images/monoidhomset.jpg}
|
||||||
\captionsetup{labelformat=empty,font=scriptsize}
|
\captionsetup{labelformat=empty,font=scriptsize}
|
||||||
\caption{Monoid hom-set seen as morphisms and as points in a set.}
|
\caption{Monoid hom-set seen as morphisms and as points in a set.}
|
||||||
\end{figure}
|
\end{figure}
|
||||||
|
|
||||||
There is just one little nit for mathematicians to pick: morphisms don't
|
There is just one little nit for mathematicians to pick: morphisms don't
|
||||||
@ -341,6 +330,7 @@ according to his and Bjarne Stroustrup's latest proposal.
|
|||||||
\section{Challenges}\label{challenges}
|
\section{Challenges}\label{challenges}
|
||||||
|
|
||||||
\begin{enumerate}
|
\begin{enumerate}
|
||||||
|
\tightlist
|
||||||
\item
|
\item
|
||||||
Generate a free category from:
|
Generate a free category from:
|
||||||
|
|
||||||
|
@ -5,15 +5,14 @@ functions that log or trace their execution. Something that, in an
|
|||||||
imperative language, would likely be implemented by mutating some global
|
imperative language, would likely be implemented by mutating some global
|
||||||
state, as in:
|
state, as in:
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
string logger;
|
string logger;
|
||||||
|
|
||||||
bool negate(bool b) {
|
bool negate(bool b) {
|
||||||
logger += "Not so! ";
|
logger += "Not so! ";
|
||||||
return !b;
|
return !b;
|
||||||
}
|
}
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
You know that this is not a pure function, because its memoized version
|
You know that this is not a pure function, because its memoized version
|
||||||
would fail to produce a log. This function has \newterm{side effects}.
|
would fail to produce a log. This function has \newterm{side effects}.
|
||||||
|
|
||||||
@ -26,28 +25,25 @@ have to pass the log explicitly, in and out. Let's do that by adding a
|
|||||||
string argument, and pairing regular output with a string that contains
|
string argument, and pairing regular output with a string that contains
|
||||||
the updated log:
|
the updated log:
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
pair<bool, string> negate(bool b, string logger) {
|
pair<bool, string> negate(bool b, string logger) {
|
||||||
return make_pair(!b, logger + "Not so! ");
|
return make_pair(!b, logger + "Not so! ");
|
||||||
}
|
}
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
This function is pure, it has no side effects, it returns the same pair
|
This function is pure, it has no side effects, it returns the same pair
|
||||||
every time it's called with the same arguments, and it can be memoized
|
every time it's called with the same arguments, and it can be memoized
|
||||||
if necessary. However, considering the cumulative nature of the log,
|
if necessary. However, considering the cumulative nature of the log,
|
||||||
you'd have to memoize all possible histories that can lead to a given
|
you'd have to memoize all possible histories that can lead to a given
|
||||||
call. There would be a separate memo entry for:
|
call. There would be a separate memo entry for:
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
negate(true, "It was the best of times. ");
|
negate(true, "It was the best of times. ");
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
and
|
and
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
negate(true, "It was the worst of times. ");
|
negate(true, "It was the worst of times. ");
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
and so on.
|
and so on.
|
||||||
|
|
||||||
It's also not a very good interface for a library function. The callers
|
It's also not a very good interface for a library function. The callers
|
||||||
@ -64,12 +60,11 @@ continuous log is a separate concern. We still want the function to
|
|||||||
produce a string, but we'd like to unburden it from producing a log. So
|
produce a string, but we'd like to unburden it from producing a log. So
|
||||||
here's the compromise solution:
|
here's the compromise solution:
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
pair<bool, string> negate(bool b) {
|
pair<bool, string> negate(bool b) {
|
||||||
return make_pair(!b, "Not so! ");
|
return make_pair(!b, "Not so! ");
|
||||||
}
|
}
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
The idea is that the log will be aggregated \emph{between} function
|
The idea is that the log will be aggregated \emph{between} function
|
||||||
calls.
|
calls.
|
||||||
|
|
||||||
@ -77,27 +72,25 @@ To see how this can be done, let's switch to a slightly more realistic
|
|||||||
example. We have one function from string to string that turns lower
|
example. We have one function from string to string that turns lower
|
||||||
case characters to upper case:
|
case characters to upper case:
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
string toUpper(string s) {
|
string toUpper(string s) {
|
||||||
string result;
|
string result;
|
||||||
int (*toupperp)(int) = &toupper; // toupper is overloaded
|
int (*toupperp)(int) = &toupper; // toupper is overloaded
|
||||||
transform(begin(s), end(s), back_inserter(result), toupperp);
|
transform(begin(s), end(s), back_inserter(result), toupperp);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
and another that splits a string into a vector of strings, breaking it
|
and another that splits a string into a vector of strings, breaking it
|
||||||
on whitespace boundaries:
|
on whitespace boundaries:
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
vector<string> toWords(string s) {
|
vector<string> toWords(string s) {
|
||||||
return words(s);
|
return words(s);
|
||||||
}
|
}
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
The actual work is done in the auxiliary function \code{words}:
|
The actual work is done in the auxiliary function \code{words}:
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
vector<string> words(string s) {
|
vector<string> words(string s) {
|
||||||
vector<string> result{""};
|
vector<string> result{""};
|
||||||
for (auto i = begin(s); i != end(s); ++i)
|
for (auto i = begin(s); i != end(s); ++i)
|
||||||
@ -109,11 +102,11 @@ vector<string> words(string s) {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
\begin{wrapfigure}{R}{0pt}
|
\begin{wrapfigure}[11]{R}{0pt}
|
||||||
\raisebox{0pt}[\dimexpr\height-0.75\baselineskip\relax]{
|
\raisebox{0pt}[\dimexpr\height-0.75\baselineskip\relax]{
|
||||||
\fbox{\includegraphics[width=1.83333in]{images/piggyback.jpg}}}%
|
\fbox{\includegraphics[width=1.83333in]{images/piggyback.jpg}}}%
|
||||||
\end{wrapfigure}
|
\end{wrapfigure}
|
||||||
|
|
||||||
We want to modify the functions \code{toUpper} and \code{toWords} so
|
We want to modify the functions \code{toUpper} and \code{toWords} so
|
||||||
@ -125,14 +118,13 @@ in a generic way by defining a template \code{Writer} that
|
|||||||
encapsulates a pair whose first component is a value of arbitrary type
|
encapsulates a pair whose first component is a value of arbitrary type
|
||||||
\code{A} and the second component is a string:
|
\code{A} and the second component is a string:
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
template<class A>
|
template<class A>
|
||||||
using Writer = pair<A, string>;
|
using Writer = pair<A, string>;
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
Here are the embellished functions:
|
Here are the embellished functions:
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
Writer<string> toUpper(string s) {
|
Writer<string> toUpper(string s) {
|
||||||
string result;
|
string result;
|
||||||
int (*toupperp)(int) = &toupper;
|
int (*toupperp)(int) = &toupper;
|
||||||
@ -143,20 +135,18 @@ Writer<string> toUpper(string s) {
|
|||||||
Writer<vector<string>> toWords(string s) {
|
Writer<vector<string>> toWords(string s) {
|
||||||
return make_pair(words(s), "toWords ");
|
return make_pair(words(s), "toWords ");
|
||||||
}
|
}
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
We want to compose these two functions into another embellished function
|
We want to compose these two functions into another embellished function
|
||||||
that uppercases a string and splits it into words, all the while
|
that uppercases a string and splits it into words, all the while
|
||||||
producing a log of those actions. Here's how we may do it:
|
producing a log of those actions. Here's how we may do it:
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
Writer<vector<string>> process(string s) {
|
Writer<vector<string>> process(string s) {
|
||||||
auto p1 = toUpper(s);
|
auto p1 = toUpper(s);
|
||||||
auto p2 = toWords(p1.first);
|
auto p2 = toWords(p1.first);
|
||||||
return make_pair(p2.first, p1.second + p2.second);
|
return make_pair(p2.first, p1.second + p2.second);
|
||||||
}
|
}
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
We have accomplished our goal: The aggregation of the log is no longer
|
We have accomplished our goal: The aggregation of the log is no longer
|
||||||
the concern of the individual functions. They produce their own
|
the concern of the individual functions. They produce their own
|
||||||
messages, which are then, externally, concatenated into a larger log.
|
messages, which are then, externally, concatenated into a larger log.
|
||||||
@ -184,35 +174,32 @@ important point is that this morphism is still considered an arrow
|
|||||||
between the objects \code{int} and \code{bool}, even though the
|
between the objects \code{int} and \code{bool}, even though the
|
||||||
embellished function returns a pair:
|
embellished function returns a pair:
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
pair<bool, string> isEven(int n) {
|
pair<bool, string> isEven(int n) {
|
||||||
return make_pair(n % 2 == 0, "isEven ");
|
return make_pair(n % 2 == 0, "isEven ");
|
||||||
}
|
}
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
By the laws of a category, we should be able to compose this morphism
|
By the laws of a category, we should be able to compose this morphism
|
||||||
with another morphism that goes from the object \code{bool} to
|
with another morphism that goes from the object \code{bool} to
|
||||||
whatever. In particular, we should be able to compose it with our
|
whatever. In particular, we should be able to compose it with our
|
||||||
earlier \code{negate}:
|
earlier \code{negate}:
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
pair<bool, string> negate(bool b) {
|
pair<bool, string> negate(bool b) {
|
||||||
return make_pair(!b, "Not so! ");
|
return make_pair(!b, "Not so! ");
|
||||||
}
|
}
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
Obviously, we cannot compose these two morphisms the same way we compose
|
Obviously, we cannot compose these two morphisms the same way we compose
|
||||||
regular functions, because of the input/output mismatch. Their
|
regular functions, because of the input/output mismatch. Their
|
||||||
composition should look more like this:
|
composition should look more like this:
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
pair<bool, string> isOdd(int n) {
|
pair<bool, string> isOdd(int n) {
|
||||||
pair<bool, string> p1 = isEven(n);
|
pair<bool, string> p1 = isEven(n);
|
||||||
pair<bool, string> p2 = negate(p1.first);
|
pair<bool, string> p2 = negate(p1.first);
|
||||||
return make_pair(p2.first, p1.second + p2.second);
|
return make_pair(p2.first, p1.second + p2.second);
|
||||||
}
|
}
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
So here's the recipe for the composition of two morphisms in this new
|
So here's the recipe for the composition of two morphisms in this new
|
||||||
category we are constructing:
|
category we are constructing:
|
||||||
|
|
||||||
@ -237,7 +224,7 @@ corresponding to three objects in our category. It should take two
|
|||||||
embellished functions that are composable according to our rules, and
|
embellished functions that are composable according to our rules, and
|
||||||
return a third embellished function:
|
return a third embellished function:
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
template<class A, class B, class C>
|
template<class A, class B, class C>
|
||||||
function<Writer<C>(A)> compose(function<Writer<B>(A)> m1,
|
function<Writer<C>(A)> compose(function<Writer<B>(A)> m1,
|
||||||
function<Writer<C>(B)> m2)
|
function<Writer<C>(B)> m2)
|
||||||
@ -248,23 +235,21 @@ function<Writer<C>(A)> compose(function<Writer<B>(A)> m1,
|
|||||||
return make_pair(p2.first, p1.second + p2.second);
|
return make_pair(p2.first, p1.second + p2.second);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
Now we can go back to our earlier example and implement the composition
|
Now we can go back to our earlier example and implement the composition
|
||||||
of \code{toUpper} and \code{toWords} using this new template:
|
of \code{toUpper} and \code{toWords} using this new template:
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{minted}[breaklines, breakafter=>>]{text}
|
||||||
Writer<vector<string>> process(string s) {
|
Writer<vector<string>> process(string s) {
|
||||||
return compose<string, string, vector<string>>(toUpper, toWords)(s);
|
return compose<string, string, vector<string>>(toUpper, toWords)(s);
|
||||||
}
|
}
|
||||||
\end{minted}
|
\end{minted}
|
||||||
|
|
||||||
There is still a lot of noise with the passing of types to the
|
There is still a lot of noise with the passing of types to the
|
||||||
\code{compose} template. This can be avoided as long as you have a
|
\code{compose} template. This can be avoided as long as you have a
|
||||||
C++14-compliant compiler that supports generalized lambda functions with
|
C++14-compliant compiler that supports generalized lambda functions with
|
||||||
return type deduction (credit for this code goes to Eric Niebler):
|
return type deduction (credit for this code goes to Eric Niebler):
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
auto const compose = [](auto m1, auto m2) {
|
auto const compose = [](auto m1, auto m2) {
|
||||||
return [m1, m2](auto x) {
|
return [m1, m2](auto x) {
|
||||||
auto p1 = m1(x);
|
auto p1 = m1(x);
|
||||||
@ -272,37 +257,33 @@ auto const compose = [](auto m1, auto m2) {
|
|||||||
return make_pair(p2.first, p1.second + p2.second);
|
return make_pair(p2.first, p1.second + p2.second);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
In this new definition, the implementation of \code{process}
|
In this new definition, the implementation of \code{process}
|
||||||
simplifies to:
|
simplifies to:
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
Writer<vector<string>> process(string s) {
|
Writer<vector<string>> process(string s) {
|
||||||
return compose(toUpper, toWords)(s);
|
return compose(toUpper, toWords)(s);
|
||||||
}
|
}
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
But we are not finished yet. We have defined composition in our new
|
But we are not finished yet. We have defined composition in our new
|
||||||
category, but what are the identity morphisms? These are not our regular
|
category, but what are the identity morphisms? These are not our regular
|
||||||
identity functions! They have to be morphisms from type A back to type
|
identity functions! They have to be morphisms from type A back to type
|
||||||
A, which means they are embellished functions of the form:
|
A, which means they are embellished functions of the form:
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
Writer<A> identity(A);
|
Writer<A> identity(A);
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
They have to behave like units with respect to composition. If you look
|
They have to behave like units with respect to composition. If you look
|
||||||
at our definition of composition, you'll see that an identity morphism
|
at our definition of composition, you'll see that an identity morphism
|
||||||
should pass its argument without change, and only contribute an empty
|
should pass its argument without change, and only contribute an empty
|
||||||
string to the log:
|
string to the log:
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
template<class A> Writer<A> identity(A x) {
|
template<class A> Writer<A> identity(A x) {
|
||||||
return make_pair(x, "");
|
return make_pair(x, "");
|
||||||
}
|
}
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
You can easily convince yourself that the category we have just defined
|
You can easily convince yourself that the category we have just defined
|
||||||
is indeed a legitimate category. In particular, our composition is
|
is indeed a legitimate category. In particular, our composition is
|
||||||
trivially associative. If you follow what's happening with the first
|
trivially associative. If you follow what's happening with the first
|
||||||
@ -325,10 +306,9 @@ The same thing in Haskell is a little more terse, and we also get a lot
|
|||||||
more help from the compiler. Let's start by defining the \code{Writer}
|
more help from the compiler. Let's start by defining the \code{Writer}
|
||||||
type:
|
type:
|
||||||
|
|
||||||
\begin{minted}{haskell}
|
\begin{verbatim}
|
||||||
type Writer a = (a, String)
|
type Writer a = (a, String)
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
Here I'm just defining a type alias, an equivalent of a \code{typedef}
|
Here I'm just defining a type alias, an equivalent of a \code{typedef}
|
||||||
(or \code{using}) in C++. The type \code{Writer} is parameterized by
|
(or \code{using}) in C++. The type \code{Writer} is parameterized by
|
||||||
a type variable \code{a} and is equivalent to a pair of \code{a} and
|
a type variable \code{a} and is equivalent to a pair of \code{a} and
|
||||||
@ -338,17 +318,15 @@ parentheses, separated by a comma.
|
|||||||
Our morphisms are functions from an arbitrary type to some
|
Our morphisms are functions from an arbitrary type to some
|
||||||
\code{Writer} type:
|
\code{Writer} type:
|
||||||
|
|
||||||
\begin{minted}{haskell}
|
\begin{verbatim}
|
||||||
a -> Writer b
|
a -> Writer b
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
We'll declare the composition as a funny infix operator, sometimes
|
We'll declare the composition as a funny infix operator, sometimes
|
||||||
called the ``fish'':
|
called the ``fish'':
|
||||||
|
|
||||||
\begin{minted}{haskell}
|
\begin{verbatim}
|
||||||
(>=>) :: (a -> Writer b) -> (b -> Writer c) -> (a -> Writer c)
|
(>=>) :: (a -> Writer b) -> (b -> Writer c) -> (a -> Writer c)
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
It's a function of two arguments, each being a function on its own, and
|
It's a function of two arguments, each being a function on its own, and
|
||||||
returning a function. The first argument is of the type
|
returning a function. The first argument is of the type
|
||||||
\code{(a->Writer b)}, the second is
|
\code{(a->Writer b)}, the second is
|
||||||
@ -359,13 +337,12 @@ Here's the definition of this infix operator --- the two arguments
|
|||||||
\code{m1} and \code{m2} appearing on either side of the fishy
|
\code{m1} and \code{m2} appearing on either side of the fishy
|
||||||
symbol:
|
symbol:
|
||||||
|
|
||||||
\begin{minted}{haskell}
|
\begin{verbatim}
|
||||||
m1 >=> m2 = \x ->
|
m1 >=> m2 = \x ->
|
||||||
let (y, s1) = m1 x
|
let (y, s1) = m1 x
|
||||||
(z, s2) = m2 y
|
(z, s2) = m2 y
|
||||||
in (z, s1 ++ s2)
|
in (z, s1 ++ s2)
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
The result is a lambda function of one argument \code{x}. The lambda
|
The result is a lambda function of one argument \code{x}. The lambda
|
||||||
is written as a backslash --- think of it as the Greek letter λ with an
|
is written as a backslash --- think of it as the Greek letter λ with an
|
||||||
amputated leg.
|
amputated leg.
|
||||||
@ -389,24 +366,22 @@ I will also define the identity morphism for our category, but for
|
|||||||
reasons that will become clear much later, I will call it
|
reasons that will become clear much later, I will call it
|
||||||
\code{return}.
|
\code{return}.
|
||||||
|
|
||||||
\begin{minted}{haskell}
|
\begin{verbatim}
|
||||||
return :: a -> Writer a
|
return :: a -> Writer a
|
||||||
return x = (x, "")
|
return x = (x, "")
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
For completeness, let's have the Haskell versions of the embellished
|
For completeness, let's have the Haskell versions of the embellished
|
||||||
functions \code{upCase} and \code{toWords}:
|
functions \code{upCase} and \code{toWords}:
|
||||||
|
|
||||||
\begin{minted}{haskell}
|
\begin{verbatim}
|
||||||
upCase :: String -> Writer String
|
upCase :: String -> Writer String
|
||||||
upCase s = (map toUpper s, "upCase ")
|
upCase s = (map toUpper s, "upCase ")
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
\begin{minted}{haskell}
|
\begin{verbatim}
|
||||||
toWords :: String -> Writer [String]
|
toWords :: String -> Writer [String]
|
||||||
toWords s = (words s, "toWords ")
|
toWords s = (words s, "toWords ")
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
The function \code{map} corresponds to the C++ \code{transform}. It
|
The function \code{map} corresponds to the C++ \code{transform}. It
|
||||||
applies the character function \code{toUpper} to the string
|
applies the character function \code{toUpper} to the string
|
||||||
\code{s}. The auxiliary function \code{words} is defined in the
|
\code{s}. The auxiliary function \code{words} is defined in the
|
||||||
@ -415,10 +390,10 @@ standard Prelude library.
|
|||||||
Finally, the composition of the two functions is accomplished with the
|
Finally, the composition of the two functions is accomplished with the
|
||||||
help of the fish operator:
|
help of the fish operator:
|
||||||
|
|
||||||
\begin{minted}{haskell}
|
\begin{verbatim}
|
||||||
process :: String -> Writer [String]
|
process :: String -> Writer [String]
|
||||||
process = upCase >=> toWords
|
process = upCase >=> toWords
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
\section{Kleisli Categories}\label{kleisli-categories}
|
\section{Kleisli Categories}\label{kleisli-categories}
|
||||||
|
|
||||||
@ -457,7 +432,7 @@ mathematical sense, so it doesn't fit the standard categorical mold. It
|
|||||||
can, however, be represented by a function that returns an embellished
|
can, however, be represented by a function that returns an embellished
|
||||||
type \code{optional}:
|
type \code{optional}:
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
template<class A> class optional {
|
template<class A> class optional {
|
||||||
bool _isValid;
|
bool _isValid;
|
||||||
A _value;
|
A _value;
|
||||||
@ -467,18 +442,16 @@ public:
|
|||||||
bool isValid() const { return _isValid; }
|
bool isValid() const { return _isValid; }
|
||||||
A value() const { return _value; }
|
A value() const { return _value; }
|
||||||
};
|
};
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
As an example, here's the implementation of the embellished function
|
As an example, here's the implementation of the embellished function
|
||||||
\code{safe\_root}:
|
\code{safe\_root}:
|
||||||
|
|
||||||
\begin{minted}{c++}
|
\begin{verbatim}
|
||||||
optional<double> safe_root(double x) {
|
optional<double> safe_root(double x) {
|
||||||
if (x >= 0) return optional<double>{sqrt(x)};
|
if (x >= 0) return optional<double>{sqrt(x)};
|
||||||
else return optional<double>{};
|
else return optional<double>{};
|
||||||
}
|
}
|
||||||
\end{minted}
|
\end{verbatim}
|
||||||
|
|
||||||
Here's the challenge:
|
Here's the challenge:
|
||||||
|
|
||||||
\begin{enumerate}
|
\begin{enumerate}
|
||||||
|
Loading…
Reference in New Issue
Block a user