From 23ba84e664ba4ced9359ea02a838d7d877a53299 Mon Sep 17 00:00:00 2001 From: Sergei Winitzki Date: Fri, 9 Aug 2024 22:08:14 +0200 Subject: [PATCH] reformatting and corrections in part 2 (#105) * wip * wip * wip * explain companion objects and non-extension methods * wip * wip * wip * correct "type constructor" in chapter 5 * wip * wip * wip * wip * wip changes * wip * wip * wip * wip --- sofp-src/lyx/sofp-appendices.lyx | 472 ++- sofp-src/lyx/sofp-applicative.lyx | 4 +- sofp-src/lyx/sofp-curry-howard.lyx | 20 +- sofp-src/lyx/sofp-essay1.lyx | 21 +- sofp-src/lyx/sofp-essay2.lyx | 6 +- sofp-src/lyx/sofp-free-type.lyx | 153 +- sofp-src/lyx/sofp-functors.lyx | 607 ++- sofp-src/lyx/sofp-induction.lyx | 2 +- sofp-src/lyx/sofp-monads.lyx | 217 +- sofp-src/lyx/sofp-reasoning.lyx | 68 +- sofp-src/lyx/sofp-summary.lyx | 519 ++- sofp-src/lyx/sofp-transformers.lyx | 30 +- sofp-src/lyx/sofp-traversable.lyx | 4789 +++++++++++++++--------- sofp-src/lyx/sofp-typeclasses.lyx | 1303 +++++-- sofp-src/tex/chapter3-picture.pdf | Bin 6720 -> 6720 bytes sofp-src/tex/sofp-appendices.tex | 384 +- sofp-src/tex/sofp-applicative.tex | 4 +- sofp-src/tex/sofp-back-cover-no-bg.tex | 8 +- sofp-src/tex/sofp-curry-howard.tex | 16 +- sofp-src/tex/sofp-essay1.tex | 13 +- sofp-src/tex/sofp-essay2.tex | 4 +- sofp-src/tex/sofp-free-type.tex | 91 +- sofp-src/tex/sofp-functors.tex | 364 +- sofp-src/tex/sofp-induction.tex | 2 +- sofp-src/tex/sofp-monads.tex | 120 +- sofp-src/tex/sofp-reasoning.tex | 49 +- sofp-src/tex/sofp-summary.tex | 151 +- sofp-src/tex/sofp-transformers.tex | 38 +- sofp-src/tex/sofp-traversable.tex | 419 ++- sofp-src/tex/sofp-typeclasses.tex | 324 +- sofp-src/tex/sofp.tex | 30 +- 31 files changed, 6577 insertions(+), 3651 deletions(-) diff --git a/sofp-src/lyx/sofp-appendices.lyx b/sofp-src/lyx/sofp-appendices.lyx index ed9e8d897..19b3e53fa 100644 --- a/sofp-src/lyx/sofp-appendices.lyx +++ b/sofp-src/lyx/sofp-appendices.lyx @@ -530,7 +530,7 @@ status open \begin_layout Plain Layout -(A,B) +(A, B) \end_layout \end_inset @@ -589,7 +589,7 @@ status open \begin_layout Plain Layout -{ x:A => f } +{ x: A => f } \end_layout \end_inset @@ -871,38 +871,10 @@ pure \end_layout \begin_layout Description -\begin_inset Formula $F^{\bullet}$ -\end_inset - - — the type constructor -\begin_inset Formula $F$ -\end_inset - - understood as a type-level function. - In Scala, -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -F[_] -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Description -\begin_inset Formula $F^{\bullet}\leadsto G^{\bullet}$ -\end_inset - - — or \begin_inset Formula $F\leadsto G$ \end_inset - a natural transformation between functors + — a natural transformation between functors \begin_inset Formula $F$ \end_inset @@ -1027,11 +999,7 @@ f compose g \end_inset — the backward composition of type constructors: -\begin_inset Formula $F\circ G$ -\end_inset - - is -\begin_inset Formula $F^{G^{\bullet}}$ +\begin_inset Formula $(F\circ G)^{A}\triangleq F^{G^{A}}$ \end_inset . @@ -1138,13 +1106,45 @@ h.map(_.map(f)) \end_inset +\end_layout + +\begin_layout Description +\begin_inset Formula $S^{\bullet,T}$ +\end_inset + + — the type constructor defined by fixing one of the type parameters of + another type constructor. + For instance: +\begin_inset Formula $(S^{\bullet,T})^{A}\triangleq S^{A,T}$ +\end_inset + +. + In Scala, +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +S[*, T] +\end_layout + +\end_inset + +. + Similarly, +\begin_inset Formula $(S^{A,\bullet})^{B}\triangleq S^{A,B}$ +\end_inset + +. + \end_layout \begin_layout Description \begin_inset Formula $\diamond_{M}$ \end_inset - — the Kleisli product operation for the monad + — the Kleisli composition operation for the monad \begin_inset Formula $M$ \end_inset @@ -1194,7 +1194,7 @@ x |+| y \begin_inset Formula $\Delta$ \end_inset - — the + — the standard \begin_inset Quotes eld \end_inset @@ -1224,14 +1224,14 @@ diagonal \begin_inset Formula $(f\boxtimes g)(a\times b)\triangleq f(a)\times g(b)$ \end_inset - +, also applies to relations \end_layout \begin_layout Description \begin_inset Formula $\boxplus$ \end_inset - — pair co-product of functions + — pair co-product of functions or relations \end_layout \begin_layout Description @@ -2358,63 +2358,29 @@ F[_] \begin_inset Formula $\bullet$ \end_inset -, is used as a placeholder for the missing type parameter. - When no type parameter is needed, -\begin_inset Formula $F$ -\end_inset - - means the same as -\begin_inset Formula $F^{\bullet}$ -\end_inset - -. - (For example, -\begin_inset Quotes eld -\end_inset - -a functor -\begin_inset Formula $F$ -\end_inset - - -\begin_inset Quotes erd -\end_inset - - and -\begin_inset Quotes eld +, is a placeholder for the missing type parameter. + When the type parameter +\begin_inset Formula $B$ \end_inset -a functor -\begin_inset Formula $F^{\bullet}$ + of a bifunctor +\begin_inset Formula $P^{A,B}$ \end_inset - -\begin_inset Quotes erd + is fixed as +\begin_inset Formula $B=Z$ \end_inset - mean the same thing.) However, it is useful for clarity to be able to indicate - the place where the type parameter would appear. - For instance, functor composition is denoted as -\begin_inset Formula $F^{G^{\bullet}}$ +, we get a functor (with respect to +\begin_inset Formula $A$ \end_inset -; in Scala 2, this is -\family typewriter - -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Lambda[X => F[G[X]]] -\end_layout - +) denoted by +\begin_inset Formula $P^{\bullet,Z}$ \end_inset - -\family default - when using the +. + In Scala, this is written using the \begin_inset Quotes eld \end_inset @@ -2440,7 +2406,7 @@ kind projector \end_inset - plugin. + plugin \begin_inset Foot status open @@ -2460,24 +2426,16 @@ literal "false" \end_inset - When the type parameter -\begin_inset Formula $B$ -\end_inset - - of a bifunctor -\begin_inset Formula $P^{A,B}$ -\end_inset + syntax as +\begin_inset listings +inline true +status open - is fixed to -\begin_inset Formula $Z$ -\end_inset +\begin_layout Plain Layout -, we get a functor (with respect to -\begin_inset Formula $A$ -\end_inset +Lambda[A => P[A, Z]] +\end_layout -) denoted by -\begin_inset Formula $P^{\bullet,Z}$ \end_inset . @@ -2525,10 +2483,6 @@ literal "false" \end_layout \begin_layout Standard -\begin_inset Formula $F^{\bullet}\leadsto G^{\bullet}$ -\end_inset - - or \begin_inset Formula $F\leadsto G$ \end_inset @@ -3061,7 +3015,7 @@ h.contramap(f) \end_inset . - Nested lifting is denoted as, e.g., + Repeated lifting is denoted as, e.g., \begin_inset Formula $f^{\downarrow H\uparrow G}\triangleq(f^{\downarrow H})^{\uparrow G}$ \end_inset @@ -3072,7 +3026,7 @@ h.contramap(f) \begin_inset Formula $\diamond_{M}$ \end_inset - means the Kleisli product operation for a given monad + means the Kleisli composition operation for a given monad \begin_inset Formula $M$ \end_inset @@ -5857,11 +5811,11 @@ ations, i.e., for functions of type \end_inset where both -\begin_inset Formula $F^{\bullet}$ +\begin_inset Formula $F$ \end_inset and -\begin_inset Formula $G^{\bullet}$ +\begin_inset Formula $G$ \end_inset are functors (or both contrafunctors). @@ -7700,11 +7654,11 @@ noprefix "false" \end_inset where -\begin_inset Formula $G^{\bullet}$ +\begin_inset Formula $G$ \end_inset and -\begin_inset Formula $H^{\bullet}$ +\begin_inset Formula $H$ \end_inset are functors then @@ -7724,11 +7678,11 @@ If \end_inset where -\begin_inset Formula $G^{\bullet}$ +\begin_inset Formula $G$ \end_inset and -\begin_inset Formula $H^{\bullet}$ +\begin_inset Formula $H$ \end_inset are contrafunctors then @@ -7768,11 +7722,11 @@ fmap \end_inset where -\begin_inset Formula $F^{\bullet}$ +\begin_inset Formula $F$ \end_inset and -\begin_inset Formula $G^{\bullet}$ +\begin_inset Formula $G$ \end_inset are functors: @@ -7925,7 +7879,7 @@ fold \end_inset The profunctor -\begin_inset Formula $P^{X,Y}$ +\begin_inset Formula $P$ \end_inset is complicated but the form of the type signature ( @@ -9322,15 +9276,15 @@ cmap \begin_layout Standard The structure of a given fully parametric type constructor -\begin_inset Formula $F^{A}$ +\begin_inset Formula $F$ \end_inset dictates a unique implementation of a lifting -\begin_inset Formula $f^{\uparrow P}$ +\begin_inset Formula $f^{\uparrow F}$ \end_inset or -\begin_inset Formula $f^{\downarrow P}$ +\begin_inset Formula $f^{\downarrow F}$ \end_inset . @@ -12151,7 +12105,7 @@ can . The lifting operation can be defined for any exponential-polynomial profunctor -\begin_inset Formula $P^{\bullet,\bullet}$ +\begin_inset Formula $P$ \end_inset . @@ -13476,7 +13430,7 @@ noprefix "false" \end_inset to an arbitrary type constructor -\begin_inset Formula $G^{\bullet}$ +\begin_inset Formula $G$ \end_inset . @@ -13928,7 +13882,7 @@ any \end_inset 's type signature (the type constructor -\begin_inset Formula $P^{\bullet}$ +\begin_inset Formula $P$ \end_inset ). @@ -14860,7 +14814,7 @@ We now turn to defining the relational lifting \end_inset for an arbitrary type constructor -\begin_inset Formula $G^{\bullet}$ +\begin_inset Formula $G$ \end_inset . @@ -14901,7 +14855,7 @@ Given a relation \end_inset and a fully parametric type constructor -\begin_inset Formula $G^{\bullet}$ +\begin_inset Formula $G$ \end_inset , the relational lifting of @@ -14939,19 +14893,19 @@ Given a relation \series default . We will use arbitrary type constructors -\begin_inset Formula $K^{A}$ +\begin_inset Formula $K$ \end_inset , -\begin_inset Formula $L^{A}$ +\begin_inset Formula $L$ \end_inset , -\begin_inset Formula $H^{X,A}$ +\begin_inset Formula $H$ \end_inset , and -\begin_inset Formula $S^{A,R}$ +\begin_inset Formula $S$ \end_inset that are assumed to be fully parametric but not necessarily covariant or @@ -15103,7 +15057,7 @@ simultaneous \end_inset to the type constructor -\begin_inset Formula $S^{\bullet,\bullet}$ +\begin_inset Formula $S$ \end_inset (see Definition @@ -15122,7 +15076,7 @@ noprefix "false" below). The inductive assumption is that simultaneous liftings to -\begin_inset Formula $S^{\bullet,\bullet}$ +\begin_inset Formula $S$ \end_inset are already defined. @@ -15442,7 +15396,7 @@ noprefix "false" \end_inset for the following type constructors -\begin_inset Formula $P^{\bullet}$ +\begin_inset Formula $P$ \end_inset : @@ -15578,7 +15532,7 @@ In each case, the lifted relation \begin_inset Formula $P^{A}$ \end_inset - is a disjunction: + is a disjunctive type : \begin_inset Formula $P^{A}=\text{Id}^{A}+H^{A}$ \end_inset @@ -17228,7 +17182,7 @@ not \end_inset with some -\begin_inset Formula $K^{\bullet}$ +\begin_inset Formula $K$ \end_inset . @@ -17638,7 +17592,7 @@ noprefix "false" \begin_layout Standard For any fully parametric type constructor -\begin_inset Formula $G^{\bullet,\bullet}$ +\begin_inset Formula $G$ \end_inset with two type parameters, we define the simultaneous lifting of two relations @@ -17721,11 +17675,11 @@ For any fully parametric type constructor (c) \series default If -\begin_inset Formula $K^{\bullet,\bullet}$ +\begin_inset Formula $K$ \end_inset and -\begin_inset Formula $L^{\bullet,\bullet}$ +\begin_inset Formula $L$ \end_inset are any fully parametric type constructors, we define: @@ -17772,7 +17726,7 @@ The inductive assumption is that simultaneous liftings to \end_inset is defined recursively via a recursion scheme -\begin_inset Formula $S^{\bullet,\bullet,\bullet}$ +\begin_inset Formula $S$ \end_inset , we define: @@ -17802,7 +17756,7 @@ Here we use three \emph default relations to -\begin_inset Formula $S^{\bullet,\bullet,\bullet}$ +\begin_inset Formula $S$ \end_inset are already defined. @@ -17852,7 +17806,7 @@ A shorter way of writing this definition is by formulating a relation between \end_inset The inductive assumption is that simultaneous liftings to -\begin_inset Formula $H^{\bullet,\bullet,\bullet}$ +\begin_inset Formula $H$ \end_inset are already defined. @@ -17993,7 +17947,7 @@ noprefix "false" \end_inset simultaneously to -\begin_inset Formula $P^{\bullet,\bullet}$ +\begin_inset Formula $P$ \end_inset according to Definition @@ -18096,7 +18050,7 @@ noprefix "false" \end_inset for the type constructor -\begin_inset Formula $P^{\bullet,\bullet}$ +\begin_inset Formula $P$ \end_inset and the corresponding cases of Definition @@ -18114,7 +18068,7 @@ noprefix "false" \end_inset for -\begin_inset Formula $G^{\bullet}$ +\begin_inset Formula $G$ \end_inset . @@ -18177,7 +18131,7 @@ If \begin_layout Standard If -\begin_inset Formula $P^{\bullet,\bullet}\triangleq K^{\bullet,\bullet}\times L^{\bullet,\bullet}$ +\begin_inset Formula $P\triangleq K\times L$ \end_inset then we have @@ -18220,7 +18174,7 @@ If \begin_layout Standard If -\begin_inset Formula $P^{\bullet,\bullet}\triangleq K^{\bullet,\bullet}+L^{\bullet,\bullet}$ +\begin_inset Formula $P\triangleq K+L$ \end_inset with @@ -18240,7 +18194,7 @@ If \begin_layout Standard If -\begin_inset Formula $P^{\bullet,\bullet}\triangleq K^{\bullet,\bullet}\rightarrow L^{\bullet,\bullet}$ +\begin_inset Formula $P\triangleq K\rightarrow L$ \end_inset with @@ -18264,11 +18218,11 @@ If \end_inset with a recursion scheme -\begin_inset Formula $S^{\bullet,\bullet,\bullet}$ +\begin_inset Formula $S$ \end_inset , the type constructor -\begin_inset Formula $G^{\bullet}$ +\begin_inset Formula $G$ \end_inset is defined by: @@ -18321,7 +18275,7 @@ If \end_inset , the type constructor -\begin_inset Formula $G^{\bullet}$ +\begin_inset Formula $G$ \end_inset is @@ -18608,11 +18562,11 @@ free variable \end_inset , where -\begin_inset Formula $P^{\bullet}$ +\begin_inset Formula $P$ \end_inset and -\begin_inset Formula $Q^{\bullet}$ +\begin_inset Formula $Q$ \end_inset are any type constructors. @@ -19904,7 +19858,7 @@ So, the relational naturality law of \end_layout \begin_layout Paragraph -Use disjunction +Use disjunctive type \end_layout \begin_layout Standard @@ -21721,10 +21675,10 @@ noprefix "false" \series default An identity relation can be removed from a simultaneous lifting. For instance, given any type constructor -\begin_inset Formula $H^{\bullet,\bullet}$ +\begin_inset Formula $H$ \end_inset -, a fixed type + with two type parameters, a fixed type \begin_inset Formula $T$ \end_inset @@ -21752,7 +21706,7 @@ noprefix "false" \begin_inset Formula \[ -\text{for any }G^{\bullet},H^{\bullet,\bullet},\text{etc}.:\quad(\text{id}^{:A\leftrightarrow A})^{\updownarrow G}=\text{id}^{:G^{A}\leftrightarrow G^{A}}\quad,\quad\quad(\text{id}^{:A\leftrightarrow A},\text{id}^{:X\leftrightarrow X})^{\updownarrow H}=\text{id}^{:H^{A,X}\leftrightarrow H^{A,X}}\quad,\quad\text{etc}. +\text{for any }G,H,\text{etc}.:\quad(\text{id}^{:A\leftrightarrow A})^{\updownarrow G}=\text{id}^{:G^{A}\leftrightarrow G^{A}}\quad,\quad\quad(\text{id}^{:A\leftrightarrow A},\text{id}^{:X\leftrightarrow X})^{\updownarrow H}=\text{id}^{:H^{A,X}\leftrightarrow H^{A,X}}\quad,\quad\text{etc}. \] \end_inset @@ -21784,7 +21738,7 @@ noprefix "false" \end_inset for -\begin_inset Formula $H^{\bullet,\bullet}$ +\begin_inset Formula $H$ \end_inset . @@ -21946,11 +21900,11 @@ If \end_inset with a recursion scheme -\begin_inset Formula $S^{\bullet,\bullet,\bullet}$ +\begin_inset Formula $S$ \end_inset , the type constructor -\begin_inset Formula $G^{\bullet}$ +\begin_inset Formula $G$ \end_inset is defined by: @@ -21979,7 +21933,7 @@ So, the lifting to \begin{align*} & (r,\text{id})^{\updownarrow H}=\big(r,\text{id},\overline{(r,\text{id})^{\updownarrow H}}\big)^{\updownarrow S}\\ \text{inductive assumption }\overline{(r,\text{id})^{\updownarrow H}}=\overline{r^{\updownarrow G}}:\quad & =\big(r,\gunderline{\text{id}},\overline{r^{\updownarrow G}}\big)^{\updownarrow S}\\ -\text{inductive assumption about lifting id to }S^{\bullet,\bullet,\bullet}:\quad & =\big(r,\overline{r^{\updownarrow G}}\big)^{\updownarrow S^{\bullet,T,\bullet}}\\ +\text{inductive assumption about lifting id to }S:\quad & =\big(r,\overline{r^{\updownarrow G}}\big)^{\updownarrow S^{\bullet,T,\bullet}}\\ \text{definition of lifting to }G:\quad & =r^{\updownarrow G}\quad. \end{align*} @@ -21994,7 +21948,7 @@ If \end_inset , the type constructor -\begin_inset Formula $G^{\bullet}$ +\begin_inset Formula $G$ \end_inset is @@ -22022,7 +21976,7 @@ If Here we used the inductive assumption that identity relations may be omitted from liftings to -\begin_inset Formula $S^{\bullet,\bullet,\bullet}$ +\begin_inset Formula $S$ \end_inset . @@ -22048,7 +22002,7 @@ noprefix "false" \end_inset for -\begin_inset Formula $G^{\bullet}$ +\begin_inset Formula $G$ \end_inset or Definition @@ -22066,7 +22020,7 @@ noprefix "false" \end_inset for -\begin_inset Formula $H^{\bullet,\bullet}$ +\begin_inset Formula $H$ \end_inset . @@ -22142,9 +22096,9 @@ The next three similar cases use the inductive assumptions : \begin_inset Formula \begin{align*} -\text{if }H^{\bullet,\bullet}\triangleq K^{\bullet,\bullet}\times L^{\bullet,\bullet}:\quad & (\text{id},\text{id})^{\updownarrow H}=(\text{id},\text{id})^{\updownarrow K}\boxtimes(\text{id},\text{id})^{\updownarrow L}=\text{id}\boxtimes\text{id}\quad;\\ -\text{if }H^{\bullet,\bullet}\triangleq K^{\bullet,\bullet}+L^{\bullet,\bullet}:\quad & (\text{id},\text{id})^{\updownarrow H}=(\text{id},\text{id})^{\updownarrow K}\boxplus(\text{id},\text{id})^{\updownarrow L}=\text{id}\boxplus\text{id}\quad;\\ -\text{if }H^{\bullet,\bullet}\triangleq K^{\bullet,\bullet}\rightarrow L^{\bullet,\bullet}:\quad & (\text{id},\text{id})^{\updownarrow H}=(\text{id},\text{id})^{\updownarrow K}\ogreaterthan(\text{id},\text{id})^{\updownarrow L}=\text{id}\ogreaterthan\text{id}\quad. +\text{if }H\triangleq K\times L:\quad & (\text{id},\text{id})^{\updownarrow H}=(\text{id},\text{id})^{\updownarrow K}\boxtimes(\text{id},\text{id})^{\updownarrow L}=\text{id}\boxtimes\text{id}\quad;\\ +\text{if }H\triangleq K+L:\quad & (\text{id},\text{id})^{\updownarrow H}=(\text{id},\text{id})^{\updownarrow K}\boxplus(\text{id},\text{id})^{\updownarrow L}=\text{id}\boxplus\text{id}\quad;\\ +\text{if }H\triangleq K\rightarrow L:\quad & (\text{id},\text{id})^{\updownarrow H}=(\text{id},\text{id})^{\updownarrow K}\ogreaterthan(\text{id},\text{id})^{\updownarrow L}=\text{id}\ogreaterthan\text{id}\quad. \end{align*} \end_inset @@ -22192,7 +22146,7 @@ If \end_inset with a recursion scheme -\begin_inset Formula $S^{\bullet,\bullet,\bullet}$ +\begin_inset Formula $S$ \end_inset , we have: @@ -22211,7 +22165,7 @@ because by the inductive assumption the identity law holds for the recursive . Another inductive assumption is that the identity law holds for the liftings to -\begin_inset Formula $S^{\bullet,\bullet,\bullet}$ +\begin_inset Formula $S$ \end_inset . @@ -29067,7 +29021,7 @@ noprefix "false" \begin_layout Standard We assume a fully parametric profunctor -\begin_inset Formula $P^{\bullet,\bullet}$ +\begin_inset Formula $P$ \end_inset , arbitrary types @@ -30339,11 +30293,11 @@ noprefix "false" are both functors. We may view -\begin_inset Formula $t:F^{A}\rightarrow G^{A}$ +\begin_inset Formula $t^{A}:F^{A}\rightarrow G^{A}$ \end_inset as a function with the type signature -\begin_inset Formula $t:P^{A,A}\rightarrow Q^{A,A}$ +\begin_inset Formula $t:\forall A.\,P^{A,A}\rightarrow Q^{A,A}$ \end_inset if we define the profunctors @@ -35091,7 +35045,7 @@ Solution \end_inset with some profunctor -\begin_inset Formula $K^{X,Y}$ +\begin_inset Formula $K$ \end_inset . @@ -35898,11 +35852,11 @@ noprefix "false" \begin_layout Standard If -\begin_inset Formula $L^{\bullet}$ +\begin_inset Formula $L$ \end_inset is a polynomial functor and -\begin_inset Formula $K^{\bullet,\bullet}$ +\begin_inset Formula $K$ \end_inset is a profunctor with the post-wedge property then the profunctor @@ -36053,11 +36007,11 @@ noprefix "false" \begin_layout Standard For any type constructors -\begin_inset Formula $G^{\bullet}$ +\begin_inset Formula $G$ \end_inset and -\begin_inset Formula $H^{\bullet}$ +\begin_inset Formula $H$ \end_inset , define @@ -36074,7 +36028,7 @@ For any type constructors \end_inset to -\begin_inset Formula $F^{\bullet}$ +\begin_inset Formula $F$ \end_inset can be expressed as @@ -36108,7 +36062,7 @@ noprefix "false" \end_inset for the type constructor -\begin_inset Formula $G^{\bullet}$ +\begin_inset Formula $G$ \end_inset , we show that @@ -36277,7 +36231,7 @@ noprefix "false" \end_inset already holds separately with respect to each type parameter of -\begin_inset Formula $S^{\bullet,\bullet}$ +\begin_inset Formula $S$ \end_inset and, in particular, a simultaneous lifting to @@ -36334,7 +36288,7 @@ noprefix "false" already holds separately for liftings with respect to each type parameter of -\begin_inset Formula $P^{\bullet,\bullet}$ +\begin_inset Formula $P$ \end_inset . @@ -36889,7 +36843,7 @@ If \end_inset where -\begin_inset Formula $S^{\bullet,\bullet}$ +\begin_inset Formula $S$ \end_inset is a polynomial bifunctor then: @@ -38111,7 +38065,7 @@ By definition of the relational co-product \end_inset must be in the same part of the disjunctive type -\begin_inset Formula $Q^{\bullet}+R^{\bullet}$ +\begin_inset Formula $Q^{...}+R^{...}$ \end_inset . @@ -38141,12 +38095,12 @@ By definition of the relational co-product \end_inset must belong to the same part of the type -\begin_inset Formula $Q^{\bullet}+R^{\bullet}$ +\begin_inset Formula $Q^{...}+R^{...}$ \end_inset . Suppose they belong to the part -\begin_inset Formula $Q^{\bullet}+\bbnum 0$ +\begin_inset Formula $Q^{...}+\bbnum 0$ \end_inset , so that @@ -38289,12 +38243,12 @@ The other possibility is that both values \begin_inset Formula $q(y_{0})$ \end_inset - belong to the second part ( -\begin_inset Formula $\bbnum 0+R^{\bullet}$ + belong to the right part ( +\begin_inset Formula $\bbnum 0+R^{...}$ \end_inset ) of the type -\begin_inset Formula $Q^{\bullet}+R^{\bullet}$ +\begin_inset Formula $Q^{...}+R^{...}$ \end_inset . @@ -38307,7 +38261,7 @@ The other possibility is that both values \end_inset are of type -\begin_inset Formula $\bbnum 0+R^{\bullet}$ +\begin_inset Formula $\bbnum 0+R^{...}$ \end_inset for all @@ -38431,6 +38385,10 @@ A type constructor \begin_inset Formula $P^{A}\triangleq\bbnum 1$ \end_inset + +\begin_inset Formula $\quad$ +\end_inset + . \end_layout @@ -38443,6 +38401,10 @@ A type constructor \begin_inset Formula $P^{A}\triangleq A$ \end_inset + +\begin_inset Formula $\quad$ +\end_inset + . \end_layout @@ -38569,7 +38531,7 @@ Proof \begin_inset Formula $P^{A}\triangleq K^{A}\times L^{A}$ \end_inset - where both + where \begin_inset Formula $K^{A}\triangleq A$ \end_inset @@ -41885,6 +41847,136 @@ noprefix "false" \end_inset +\end_layout + +\begin_layout Subsubsection* +Exercise +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Exercise-1-monads-3" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + +\end_layout + +\begin_layout Standard +As a counterexample, choose +\begin_inset Formula $S=$ +\end_inset + + +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Option +\end_layout + +\end_inset + + and define +\begin_inset Formula $g$ +\end_inset + + as: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +def g: Option[Int] => Option[Int] = { _ => Some(123) } +\end_layout + +\end_inset + + +\begin_inset Formula +\[ +g:\bbnum 1+\text{Int}\rightarrow\bbnum 1+\text{Int}\quad,\quad\quad g\triangleq\_\rightarrow\bbnum 0+123^{:\text{Int}}\quad. +\] + +\end_inset + +Then apply the functions +\begin_inset Formula $g^{\uparrow S}\bef\text{ftn}_{S}$ +\end_inset + + and +\begin_inset Formula $\text{ftn}_{S}\bef g$ +\end_inset + + to the value +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +x = None +\end_layout + +\end_inset + + of type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Option[Option[Int]] +\end_layout + +\end_inset + + (in the code notation, +\begin_inset Formula $x\triangleq\bbnum 1+\bbnum 0^{:\bbnum 1+\text{Int}}$ +\end_inset + +): +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +val x: Option[Option[Int]] = None +\end_layout + +\begin_layout Plain Layout + +assert(x.map(g).flatten == None) +\end_layout + +\begin_layout Plain Layout + +assert(g(x.flatten) == Some(123)) +\end_layout + +\end_inset + + +\begin_inset Formula +\begin{align*} + & x\triangleright g^{\uparrow S}\bef\text{ftn}_{S}=(\bbnum 1+\bbnum 0^{:\bbnum 1+\text{Int}})\triangleright\text{ftn}_{S}=\bbnum 1+\bbnum 0^{:\text{Int}}\quad,\\ + & x\triangleright\text{ftn}_{S}\bef g=(\bbnum 1+\bbnum 0^{:\text{Int}})\triangleright g=\bbnum 0+123^{:\text{Int}}\quad. +\end{align*} + +\end_inset + +We find that +\begin_inset Formula $g^{\uparrow S}\bef\text{ftn}_{S}\neq\text{ftn}_{S}\bef g$ +\end_inset + +. + \end_layout \begin_layout Subsubsection* diff --git a/sofp-src/lyx/sofp-applicative.lyx b/sofp-src/lyx/sofp-applicative.lyx index e8c56947a..5b6ba517e 100644 --- a/sofp-src/lyx/sofp-applicative.lyx +++ b/sofp-src/lyx/sofp-applicative.lyx @@ -367,7 +367,7 @@ flatMap \end_inset - methods from sequences to other type constructors that support such methods + methods from sequences to other type constructors that support those methods obeying suitable laws. Following the same path, we now turn to the \begin_inset listings @@ -395,7 +395,7 @@ zip \end_inset is most often used with sequences, many other type constructors can also - have a suitable + have a lawful \begin_inset listings inline true status open diff --git a/sofp-src/lyx/sofp-curry-howard.lyx b/sofp-src/lyx/sofp-curry-howard.lyx index 0d1219519..7a0468df2 100644 --- a/sofp-src/lyx/sofp-curry-howard.lyx +++ b/sofp-src/lyx/sofp-curry-howard.lyx @@ -5495,7 +5495,7 @@ status open \begin_layout Plain Layout -F[A] +F \end_layout \end_inset @@ -25581,7 +25581,15 @@ decidable logic i.e., it has an algorithm that either finds a proof or disproves any given formula. However, that logic cannot handle type constructors such as -\begin_inset Formula $\text{Array}^{A}$ +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Array +\end_layout + \end_inset . @@ -27426,7 +27434,7 @@ Solution \end_layout \begin_layout Standard -Begin by defining a type alias for the type constructor +Begin by defining a type alias for the type \begin_inset Formula $\text{Reader}^{E,A}$ \end_inset @@ -28658,7 +28666,7 @@ Solution \begin_layout Standard For a type constructor, say, -\begin_inset Formula $P^{A}$ +\begin_inset Formula $P$ \end_inset , the standard type signatures for @@ -29918,7 +29926,7 @@ status open \begin_layout Plain Layout -P[A] +P \end_layout \end_inset @@ -29969,7 +29977,7 @@ noprefix "false" \begin_layout Standard For the type constructor -\begin_inset Formula $Q^{T,A}$ +\begin_inset Formula $Q$ \end_inset defined in Exercise diff --git a/sofp-src/lyx/sofp-essay1.lyx b/sofp-src/lyx/sofp-essay1.lyx index f47546d94..9e1f416c0 100644 --- a/sofp-src/lyx/sofp-essay1.lyx +++ b/sofp-src/lyx/sofp-essay1.lyx @@ -986,9 +986,9 @@ name "fig:The-Pyongyang-method-of-error-free-programming-1" \begin_layout Standard It is interesting to note that most older programming languages (C/C++, Java, JavaScript, Python) do not support some of these composite types. - In other words, these programming languages have type systems based on + In other words, those programming languages have type systems based on an incomplete logic. - As a result, users of these languages have to implement burdensome workarounds + As a result, users of those languages have to implement burdensome workarounds that make for error-prone code. Failure to follow mathematical principles has real costs (Figure \begin_inset space ~ @@ -1147,7 +1147,7 @@ industrial group are based on existing and mature software ecosystems: F# on .NET, Scala on JVM, and Swift on the MacOS/iOS platform. - One of the important design requirements for these languages is 100% binary + One of the important design requirements for those languages is 100% binary compatibility with their \begin_inset Quotes eld \end_inset @@ -1226,13 +1226,20 @@ object-oriented programming \end_inset paradigm. - At the same time, these new languages still have logically complete type - systems, which gives developers an important benefit of type-safe domain - modeling. + At the same time, those new +\begin_inset Quotes eld +\end_inset + +industrial +\begin_inset Quotes erd +\end_inset + + functional languages still have logically complete type systems, which + gives developers an important benefit of type-safe domain modeling. \end_layout \begin_layout Standard -Nevertheless, the type systems of these languages are not equally powerful. +Nevertheless, the type systems of those languages are not equally powerful. For instance, F# and Swift are similar to OCaml in many ways but omit OCaml's parameterized modules and some other features. Of all the mentioned languages, only Scala and Haskell directly support diff --git a/sofp-src/lyx/sofp-essay2.lyx b/sofp-src/lyx/sofp-essay2.lyx index 697bb71f8..61337ffd6 100644 --- a/sofp-src/lyx/sofp-essay2.lyx +++ b/sofp-src/lyx/sofp-essay2.lyx @@ -2380,12 +2380,12 @@ literal "false" \begin_layout Standard Looking at the situation in construction business in the U.S.A., we find that it employs about -\begin_inset Formula $10$ +\begin_inset Formula $40$ \end_inset times more construction workers as architects. - We might conclude that perhaps one software engineer is required per dozen - software artisans. + We might conclude that perhaps one software engineer is required per several + dozen software artisans. \end_layout \begin_layout Standard diff --git a/sofp-src/lyx/sofp-free-type.lyx b/sofp-src/lyx/sofp-free-type.lyx index c46778689..6b7d8f34f 100644 --- a/sofp-src/lyx/sofp-free-type.lyx +++ b/sofp-src/lyx/sofp-free-type.lyx @@ -18794,33 +18794,34 @@ Given a functor \series bold -algebra \series default - if there exists a morphism -\begin_inset Formula $p_{M}:F^{M}\rightarrow M$ + if +\begin_inset Index idx +status open + +\begin_layout Plain Layout +\begin_inset Formula $F$ \end_inset -. - The type -\begin_inset Formula $M$ +-algebra!see functor algebra +\end_layout + \end_inset - is the -\series bold -carrier -\series default - and the morphism + there exists a morphism +\begin_inset Formula $p_{M}:F^{M}\rightarrow M$ +\end_inset + +. + The morphism \begin_inset Formula $p_{M}$ \end_inset - is the + is called the \begin_inset Index idx status open \begin_layout Plain Layout -structure map of -\begin_inset Formula $F$ -\end_inset - --algebra +structure map of functor algebra \end_layout \end_inset @@ -18839,10 +18840,7 @@ structure map status open \begin_layout Plain Layout -\begin_inset Formula $F$ -\end_inset - --algebra!structure map +functor algebra!structure map \end_layout \end_inset @@ -18895,10 +18893,7 @@ algebra morphisms status open \begin_layout Plain Layout -\begin_inset Formula $F$ -\end_inset - --algebra!morphism +functor algebra!morphism \end_layout \end_inset @@ -31445,7 +31440,7 @@ noprefix "false" \begin_layout Standard Describe the monoid type class via a method functor -\begin_inset Formula $C^{\bullet}$ +\begin_inset Formula $C$ \end_inset (such that the monoid's operations are combined into the type @@ -31454,7 +31449,7 @@ Describe the monoid type class via a method functor ). Using -\begin_inset Formula $S^{\bullet}$ +\begin_inset Formula $S$ \end_inset , implement the free monoid on a type @@ -31487,7 +31482,7 @@ noprefix "false" \begin_layout Standard Assuming that -\begin_inset Formula $F^{\bullet}$ +\begin_inset Formula $F$ \end_inset is a functor, define @@ -31548,7 +31543,7 @@ q2f \end_inset - in order to compare values of + in order to compare values of type \begin_inset Formula $Q^{A}$ \end_inset @@ -32446,8 +32441,7 @@ Working with quantified types \end_layout \begin_layout Standard -In the notation used in this book, there is a key difference between the - quantified type +There is an important difference between the quantified type \begin_inset Formula $\forall X.\,F^{X}$ \end_inset @@ -33163,7 +33157,7 @@ Yoneda identity!for functors \begin_layout Standard Assume that -\begin_inset Formula $P^{\bullet}$ +\begin_inset Formula $P$ \end_inset is any type constructor and @@ -33192,11 +33186,11 @@ higher-order functor . That is, -\begin_inset Formula $S^{F^{\bullet}}$ +\begin_inset Formula $S^{F}$ \end_inset is a type that depends covariantly on an arbitrary type constructor -\begin_inset Formula $F^{\bullet}$ +\begin_inset Formula $F$ \end_inset . @@ -33205,19 +33199,20 @@ higher-order functor \end_inset is -\begin_inset Formula $S^{F^{\bullet}}\triangleq F^{\text{Int}}\times F^{\text{String}}$ +\begin_inset Formula $S^{F}\triangleq F^{\text{Int}}\times F^{\text{String}}$ \end_inset .) Then the type -\begin_inset Formula $S^{P^{\bullet}}$ +\begin_inset Formula $S^{P}$ \end_inset is equivalent to the function type -\begin_inset Formula $\forall F^{\bullet}.\,(P^{\bullet}\leadsto F^{\bullet})\rightarrow S^{F^{\bullet}}$ +\begin_inset Formula $\forall F.\,(P\leadsto F)\rightarrow S^{F}$ \end_inset -, where the function is required to be natural in the parameter -\begin_inset Formula $F^{\bullet}$ +, where the function is required to be natural in the (type costructor) + parameter +\begin_inset Formula $F$ \end_inset . @@ -33226,23 +33221,23 @@ higher-order functor \end_inset of type -\begin_inset Formula $\forall F^{\bullet}.\,(P^{\bullet}\leadsto F^{\bullet})\rightarrow S^{F}$ +\begin_inset Formula $\forall F.\,(P\leadsto F)\rightarrow S^{F}$ \end_inset involves arbitrary type constructors -\begin_inset Formula $Q^{\bullet}$ +\begin_inset Formula $Q$ \end_inset , -\begin_inset Formula $R^{\bullet}$ +\begin_inset Formula $R$ \end_inset , and arbitrary functions -\begin_inset Formula $f:P^{\bullet}\leadsto Q^{\bullet}$ +\begin_inset Formula $f:P\leadsto Q$ \end_inset and -\begin_inset Formula $g:Q^{\bullet}\leadsto R^{\bullet}$ +\begin_inset Formula $g:Q\leadsto R$ \end_inset , and may be written as @@ -33258,11 +33253,11 @@ Here, \end_inset has type -\begin_inset Formula $S^{Q^{\bullet}}\rightarrow S^{R^{\bullet}}$ +\begin_inset Formula $S^{Q}\rightarrow S^{R}$ \end_inset and is a lifting of the function -\begin_inset Formula $g:Q^{\bullet}\leadsto R^{\bullet}$ +\begin_inset Formula $g:Q\leadsto R$ \end_inset to the higher-order functor @@ -33283,19 +33278,19 @@ Here, not \emph default have to be natural transformations, and the type constructors -\begin_inset Formula $F^{\bullet}$ +\begin_inset Formula $F$ \end_inset , -\begin_inset Formula $P^{\bullet}$ +\begin_inset Formula $P$ \end_inset , -\begin_inset Formula $Q^{\bullet}$ +\begin_inset Formula $Q$ \end_inset , -\begin_inset Formula $R^{\bullet}$ +\begin_inset Formula $R$ \end_inset are @@ -33310,24 +33305,7 @@ Proof \end_layout \begin_layout Standard -***rewrite the proof in a simpler way like in chapter 10***For brevity, - we will write just -\begin_inset Formula $F$ -\end_inset - - and -\begin_inset Formula $P$ -\end_inset - - instead of -\begin_inset Formula $F^{\bullet}$ -\end_inset - - and -\begin_inset Formula $P^{\bullet}$ -\end_inset - -. +***rewrite the proof in a simpler way like in chapter 10*** \end_layout \begin_layout Standard @@ -33556,19 +33534,25 @@ recursive type equation \begin_inset Formula $T\cong F^{T}$ \end_inset -, where +, where the type constructor \begin_inset Formula $F$ \end_inset is a -\begin_inset Quotes eld -\end_inset +\series bold +recursion scheme +\series default + +\begin_inset Index idx +status open + +\begin_layout Plain Layout +recursion scheme +\end_layout -structure functor -\begin_inset Quotes erd \end_inset - that specifies the details of the type recursion. + that specifies how the type uses itself in its recursive definition. A solution of the type equation \begin_inset Formula $T\cong F^{T}$ \end_inset @@ -34334,19 +34318,28 @@ List[A] \end_inset We can write that equation in the form -\begin_inset Formula $\text{List}^{A}\cong F^{\text{List}^{A}}$ +\begin_inset Formula $T\cong F^{T}$ \end_inset where \begin_inset Formula $F^{R}\triangleq\bbnum 1+A\times R$ \end_inset -, and we consider +. + Considering \begin_inset Formula $A$ \end_inset - to be a fixed type. - Consider a modified recursion scheme: + to be a fixed type, we find +\begin_inset Formula $T=\text{List}^{A}$ +\end_inset + +. + +\end_layout + +\begin_layout Standard +Consider a modified recursion scheme: \begin_inset Formula \[ G^{R}\triangleq P+A\times R\quad, @@ -34376,7 +34369,7 @@ Solution \begin_layout Standard First, we will guess the answer. - Intuitively, we imagine + Intuitively, we imagine the type \begin_inset Formula $\text{List}^{A}$ \end_inset @@ -34609,11 +34602,11 @@ noprefix "false" \begin_layout Standard Let -\begin_inset Formula $F^{\bullet,\bullet}$ +\begin_inset Formula $F$ \end_inset be a bifunctor and define -\begin_inset Formula $P^{\bullet}$ +\begin_inset Formula $P$ \end_inset and diff --git a/sofp-src/lyx/sofp-functors.lyx b/sofp-src/lyx/sofp-functors.lyx index ee8225710..324541214 100644 --- a/sofp-src/lyx/sofp-functors.lyx +++ b/sofp-src/lyx/sofp-functors.lyx @@ -432,7 +432,7 @@ status open \begin_layout Plain Layout -{x => x * 2} +{ x => x * 2 } \end_layout \end_inset @@ -461,7 +461,7 @@ status open \begin_layout Plain Layout -Seq +List \end_layout \end_inset @@ -606,7 +606,7 @@ status open \begin_layout Plain Layout -Seq +List \end_layout \end_inset @@ -643,7 +643,7 @@ status open \begin_layout Plain Layout -Seq[A] +List[A] \end_layout \end_inset @@ -730,7 +730,7 @@ status open \begin_layout Plain Layout -Seq +List \end_layout \end_inset @@ -912,7 +912,7 @@ status open \begin_layout Plain Layout -Seq +List \end_layout \end_inset @@ -2967,7 +2967,7 @@ status open \begin_layout Plain Layout -Seq +List \end_layout \end_inset @@ -2984,7 +2984,7 @@ Try \end_inset -, and +, \begin_inset listings inline true status open @@ -3008,7 +3008,7 @@ map \end_inset - but the functor laws remain the same. +, but the functor laws remain the same. We use the subscript \begin_inset Formula $L$ \end_inset @@ -3510,7 +3510,7 @@ map \end_inset . - Here is a usage example: + Test this code: \begin_inset listings inline false status open @@ -5449,19 +5449,27 @@ the same . The overline is used only as a reminder that certain parts of code are - recursive calls. + recursive calls for which the inductive assumptions hold. \end_layout \begin_layout Standard To verify the identity law, we apply -\begin_inset Formula $\text{fmap}$ +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +fmap +\end_layout + \end_inset to an identity function ( \begin_inset Formula $\text{id}^{:A\rightarrow A}$ \end_inset -) and find: +): \begin_inset Formula \[ \text{fmap}\,(\text{id})=\,\begin{array}{|c||cc|} @@ -6432,62 +6440,107 @@ status open \begin_layout Plain Layout -for { x <- p; ... - } yield expr(x) +val result = for { x <- List(1, 2, 3); y = x * x } yield y \end_layout \end_inset -appears to compute the value +appears to return the value \begin_inset listings inline true status open \begin_layout Plain Layout -expr(x) +y \end_layout \end_inset because the code says +\begin_inset Quotes eld +\end_inset + + \begin_inset listings inline true status open \begin_layout Plain Layout -yield expr(x) +yield y \end_layout \end_inset + +\begin_inset Quotes erd +\end_inset + . However, this is not so. - As the above examples show, if + The type of \begin_inset listings inline true status open \begin_layout Plain Layout -p +result \end_layout \end_inset - is a sequence then the functor block also computes a + in this example is +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +List[Int] +\end_layout + +\end_inset + +, not +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Int +\end_layout + +\end_inset + +. + The code: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +for { x <- p ; ... + } yield f(x) +\end_layout + +\end_inset + +computes a \emph on sequence \emph default -; that will be a sequence of values of the form + of values of the form \begin_inset listings inline true status open \begin_layout Plain Layout -expr(x) +f(x) \end_layout \end_inset @@ -6559,7 +6612,7 @@ status open \begin_layout Plain Layout -L[_] +L \end_layout \end_inset @@ -6577,7 +6630,7 @@ L[B] \end_inset - where +, where \begin_inset listings inline true status open @@ -7142,7 +7195,7 @@ Functor blocks and functor laws \end_layout \begin_layout Standard -There is an important connection between the functor laws and the properties +There is a direct connection between the functor laws and the properties of code in functor blocks. Consider this code: \begin_inset listings @@ -7195,18 +7248,26 @@ res0: List[Int] = List(99, 399, 899) \end_inset -The code says that +The code says +\begin_inset Quotes eld +\end_inset + + \begin_inset listings inline true status open \begin_layout Plain Layout -x = y +y = x \end_layout \end_inset + +\begin_inset Quotes erd +\end_inset + , so it appears reasonable to eliminate \begin_inset listings inline true @@ -7251,6 +7312,10 @@ res1: List[Int] = List(99, 399, 899) \end_inset + +\end_layout + +\begin_layout Standard Another example of a reasonable refactoring is to combine transformations: \begin_inset listings inline false @@ -7356,21 +7421,33 @@ res3: List[Int] = List(120, 440, 960) \end_inset + +\end_layout + +\begin_layout Standard Looking at these code changes, we expect that the computed results will remain the same. - Indeed, when the code directly states that + Indeed, when the code defines that +\begin_inset Quotes eld +\end_inset + + \begin_inset listings inline true status open \begin_layout Plain Layout -x = y +y = x \end_layout \end_inset -, it would be confusing and counter-intuitive if the result value changed + +\begin_inset Quotes erd +\end_inset + +, it would be confusing and counter-intuitive if the result values changed after replacing \begin_inset listings inline true @@ -7395,7 +7472,7 @@ x \end_inset -. + at some other place in the code. When the code says that \begin_inset listings inline true @@ -7432,7 +7509,7 @@ f(x + 1) \end_inset - without affecting the results. + anywhere without affecting the results. \end_layout \begin_layout Standard @@ -8368,8 +8445,8 @@ f \begin_layout Standard Functor laws guarantee that we can correctly understand and modify code - written in functor blocks, reasoning about transformations of values as - we do in mathematics. + in functor blocks, reasoning about transformations of values according + to the standard logic of mathematics. \end_layout \begin_layout Subsection @@ -8534,11 +8611,11 @@ map \end_layout \begin_layout Standard -Consider the type constructor +Consider this type constructor \begin_inset Formula $H$ \end_inset - defined by: +: \begin_inset listings inline false status open @@ -8614,19 +8691,31 @@ fully parametric!function \end_inset fully parametric function needs to treat all types as type parameters. - The code: + We could satisfy the type signature of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +map +\end_layout + +\end_inset + + by this code: \begin_inset listings inline false status open \begin_layout Plain Layout -def map[A, B]: H[A] => (A => B) => H[B] = { r => f => C(_ => 123) } +def map[A, B]: H[A] => (A => B) => H[B] = { r => f => H(_ => 123) } \end_layout \end_inset -satisfies the type signature of +But this code violates the identity law of \begin_inset listings inline true status open @@ -8638,7 +8727,8 @@ map \end_inset - but is not fully parametric because it returns a specific value +. + We can also see that this code returns a specific value \begin_inset listings inline true status open @@ -8662,7 +8752,7 @@ Int \end_inset -, which is not allowed. +, which is not allowed in fully parametric code. Replacing the type \begin_inset listings inline true @@ -9375,19 +9465,7 @@ map \begin_inset Formula $Q$ \end_inset - is not a functor (we cannot implement -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -map -\end_layout - -\end_inset - - that satisfies the laws). + is not a functor. \end_layout \begin_layout Paragraph @@ -9442,8 +9520,17 @@ status open \begin_layout Plain Layout -def map[A, B](p: (A, A))(f: A => B): (B, B) = p match { case (x, y) => (f(y), - f(x)) } +def map[A, B](p: (A, A))(f: A => B): (B, B) = p match { +\end_layout + +\begin_layout Plain Layout + + case (x, y) => (f(y), f(x)) +\end_layout + +\begin_layout Plain Layout + +} \end_layout \end_inset @@ -9472,7 +9559,7 @@ status open \end_inset We should not have swapped the order of values in the pair. - The correct implementation of + The correct code for \begin_inset listings inline true status open @@ -9523,8 +9610,8 @@ map \end_inset that reorders some parts of a tuple and duplicates other parts. - The correct implementation preserves the order of parts in a tuple and - neither duplicates nor omits any data. + The correct code preserves the order of parts in a tuple and neither duplicates + nor omits any data. \end_layout \begin_layout Standard @@ -9578,7 +9665,7 @@ None \end_inset , losing information and violating the identity law. - However, we have already seen that + However, \begin_inset listings inline true status open @@ -9590,7 +9677,7 @@ Option \end_inset - has a different implementation of + has a standard implementation of \begin_inset listings inline true status open @@ -10522,14 +10609,14 @@ final case class OnlyA[A, B](eab: Either[A, B]) { \begin_layout Plain Layout - case (Left(a1), OnlyA(Left(a2))) => a1 == a2 // Values Left(a1) and - Left(a2) might be equal. + case (Left(a1), OnlyA(Left(a2))) => a1 == a2 // We have OnlyA(Left(a1)) + == OnlyA(Left(a2)) when a1 == a2. \end_layout \begin_layout Plain Layout - case _ => false // Never equal if not - both `Left`. + case _ => false // Never equal in any + other case. \end_layout \begin_layout Plain Layout @@ -10567,12 +10654,12 @@ status open \begin_layout Plain Layout -OnlyA +OnlyA(Right(...)) \end_layout \end_inset - are never equal to each other: + are not equal to themselves: \begin_inset listings inline false status open @@ -11227,7 +11314,7 @@ contramap \end_inset - with a different type signature ,where the function type is + with a different type signature, where the function type is \begin_inset Formula $B\rightarrow A$ \end_inset @@ -11291,7 +11378,7 @@ cmap \begin_inset Formula \begin{align} & \text{cmap}^{A,B}:\left(B\rightarrow A\right)\rightarrow H^{A}\rightarrow H^{B}\quad,\nonumber \\ - & \text{cmap}\triangleq f^{:B\rightarrow A}\rightarrow h^{:A\rightarrow\text{Int}}\rightarrow\left(f\bef h\right)^{:B\rightarrow\text{Int}}\quad.\label{eq:f-example-1-contrafmap} + & \text{cmap}\triangleq f^{:B\rightarrow A}\rightarrow h^{:A\rightarrow\text{Int}}\rightarrow f\bef h\quad.\label{eq:f-example-1-contrafmap} \end{align} \end_inset @@ -11743,7 +11830,8 @@ wrapped \begin_inset Quotes erd \end_inset -: A type parameter to the left of a function arrow is being +. + Type parameters to the left of a function arrow are \begin_inset Quotes eld \end_inset @@ -11751,8 +11839,8 @@ consumed \begin_inset Quotes erd \end_inset -; a type parameter to the right of a function arrow (or used without a function - arrow) is being +; type parameters to the right of a function arrow (or used without a function + arrow) are \begin_inset Quotes eld \end_inset @@ -11927,15 +12015,15 @@ name "subsec:Covariance,-contravariance,-and-subtyping" \end_layout \begin_layout Standard -Ordinarily, applying a function of type +Ordinarily, it is a type error to apply a function of type \begin_inset Formula $Q\rightarrow R$ \end_inset to a value of a different type -\begin_inset Formula $P$ +\begin_inset Formula $P\text{\ensuremath{\neq Q}}$ \end_inset - is an error: + : \begin_inset listings inline false status open @@ -11957,7 +12045,7 @@ h(p) // Type error: expected type Q but found P. \end_inset -However, the Scala compiler admits this kind of code when +However, the Scala compiler accepts this kind of code when \begin_inset Formula $P$ \end_inset @@ -11983,11 +12071,11 @@ Programming languages that support subtyping allow us to use a value of \begin_inset Formula $P$ \end_inset - in any expression instead of a value of type + instead of a value of type \begin_inset Formula $Q$ \end_inset -, when + in any expression, as long as \begin_inset Formula $P$ \end_inset @@ -11996,8 +12084,8 @@ Programming languages that support subtyping allow us to use a value of \end_inset . - Each programming language defines in some way what are the possible subtypes - of every given type. + Each programming language defines in some way the subtypes of every given + type. \end_layout @@ -12028,7 +12116,7 @@ conversion function \emph on define \emph default - subtyping through the existence of a conversion function: + subtyping through the existence of conversion functions: \end_layout \begin_layout Subsubsection @@ -12161,6 +12249,8 @@ Programming languages that support subtyping usually do not let programmers \begin_layout Standard In most programming languages, the allowed subtype conversion functions do not transform any data but only reassign the types. + So, the compiler will not need to insert any additional machine code. + The subtype conversion does not lead to any decrease in performance. Let us look at some examples of subtyping of that kind. \end_layout @@ -12289,7 +12379,7 @@ def f2: Two => AtMostTwo = { case Two(x, y) => Two(x, y) } The implementations of these subtype conversions look like the code of identity functions except for the reassignment of types. - In the matrix code notation, we write: + In the matrix code notation: \size small \begin_inset Formula @@ -12693,8 +12783,7 @@ f2 is equivalent to an identity function, this conversion does not change any data and only reassigns some types. - So, the compiler will not need to insert any additional machine code. - The subtype conversion does not lead to any decrease in performance. + \end_layout \begin_layout Standard @@ -12771,14 +12860,14 @@ noprefix "false" \end_inset -) exists but never actually needs to be applied. +) never actually needs to be applied. The Scala compiler recognizes this automatically. \end_layout \begin_layout Standard Let us now consider subtyping for type constructors. If a type constructor -\begin_inset Formula $L^{A}$ +\begin_inset Formula $L$ \end_inset is a functor, we can use its @@ -12855,12 +12944,8 @@ Since \begin_inset Formula $A$ \end_inset -, the type -\begin_inset Formula $F^{\bbnum 0}$ -\end_inset - - is a subtype of -\begin_inset Formula $F^{A}$ +, we have +\begin_inset Formula $F^{\bbnum 0}\lesssim F^{A}$ \end_inset for any functor @@ -12872,7 +12957,7 @@ Since \begin_layout Standard If a type constructor -\begin_inset Formula $H^{A}$ +\begin_inset Formula $H$ \end_inset is a contrafunctor, a subtype conversion function @@ -12882,12 +12967,12 @@ If a type constructor is lifted to: \begin_inset Formula \[ -\text{cmap}_{H}(f):H^{Q}\rightarrow H^{P}\quad, +\text{cmap}_{H}(f):H^{Q}\rightarrow H^{P}\quad. \] \end_inset -showing that +So, \begin_inset Formula $H^{Q}$ \end_inset @@ -13375,7 +13460,7 @@ Counted \end_inset . - Before doing that, the Scala compiler will check that + While doing that, the Scala compiler will check that \begin_inset listings inline true status open @@ -13388,7 +13473,7 @@ Counted \end_inset is in fact covariant. - It will be a type error if a variance annotation specified by the programmer + It is a type error if a variance annotation specified by the programmer does not match the actual covariance or contravariance of the type constructor: \begin_inset listings inline false @@ -15048,7 +15133,14 @@ To derive code transforming \text{nameless function}:\quad & =f^{:A\rightarrow C}\rightarrow g^{:\left(A\rightarrow\text{Int}\right)\rightarrow Z}\rightarrow p^{:C\rightarrow\text{Int}}\rightarrow\gunderline{\text{???}^{:Z}}\\ \text{apply }g\text{ to get a }Z:\quad & =f^{:A\rightarrow C}\rightarrow g^{:\left(A\rightarrow\text{Int}\right)\rightarrow Z}\rightarrow p^{:C\rightarrow\text{Int}}\rightarrow g(\gunderline{\text{???}^{:A\rightarrow\text{Int}}})\\ \text{nameless function}:\quad & =f^{:A\rightarrow C}\rightarrow g^{:\left(A\rightarrow\text{Int}\right)\rightarrow Z}\rightarrow p^{:C\rightarrow\text{Int}}\rightarrow g(a^{:A}\rightarrow\gunderline{\text{???}^{:\text{Int}}})\\ -\text{apply }p\text{ to get an Int}:\quad & =f^{:A\rightarrow C}\rightarrow g^{:\left(A\rightarrow\text{Int}\right)\rightarrow Z}\rightarrow p^{:C\rightarrow\text{Int}}\rightarrow g(a^{:A}\rightarrow p(\gunderline{\text{???}^{:C}}))\\ +\text{apply }p\text{ to get an Int}:\quad & =f^{:A\rightarrow C}\rightarrow g^{:\left(A\rightarrow\text{Int}\right)\rightarrow Z}\rightarrow p^{:C\rightarrow\text{Int}}\rightarrow g(a^{:A}\rightarrow p(\gunderline{\text{???}^{:C}})) +\end{align*} + +\end_inset + + +\begin_inset Formula +\begin{align*} \text{apply }f\text{ to get a }C:\quad & =f^{:A\rightarrow C}\rightarrow g^{:\left(A\rightarrow\text{Int}\right)\rightarrow Z}\rightarrow p^{:C\rightarrow\text{Int}}\rightarrow g(a^{:A}\rightarrow p(f(\gunderline{\text{???}^{:A}})))\\ \text{use argument }a^{:A}:\quad & =f\rightarrow g\rightarrow p\rightarrow g(a\rightarrow p(f(a))\quad. \end{align*} @@ -15174,7 +15266,7 @@ The analysis is simpler for the type parameter \end_inset . - Implementing the corresponding + Writing the corresponding \begin_inset listings inline true status open @@ -15228,7 +15320,7 @@ def fmapB[Z, B, C](f: B => C): Data2[Z, A] => Data2[Z, B] = { \begin_layout Plain Layout - (newE, newG) // This has type Data2[C, Z]. + (newE, newG) // This has type Data2[Z, B]. \end_layout \begin_layout Plain Layout @@ -15573,7 +15665,7 @@ def fmap[A, B](f: A => B): Either[A, A] => Either[B, B] = { \begin_layout Plain Layout - case Right(a: Int) => Left(f(a + 1)) + case Right(a: Int) => Right(f(a + 1)) \end_layout \begin_layout Plain Layout @@ -15808,8 +15900,8 @@ map \end_inset function. - How can we recognize quickly whether a given type constructor is a functor - or a contrafunctor? For example, consider the type constructor + How can we recognize quickly whether a given type constructor is a functor? + For example, consider the type constructor \begin_inset Formula $Z^{A,R}$ \end_inset @@ -16260,7 +16352,11 @@ The lifting notation for a contrafunctor \begin_inset Formula $C$ \end_inset - is: + uses a downward arrow ( +\begin_inset Formula $^{\downarrow C}$ +\end_inset + +): \begin_inset Formula \[ f^{\downarrow C}\triangleq\text{cmap}_{C}(f)\quad. @@ -16371,7 +16467,24 @@ If we fix the type parameter \end_inset . - We see that the type constructor + The bullet symbol ( +\begin_inset Formula $\bullet$ +\end_inset + +) denotes the position of the free type parameter. + So, +\begin_inset Formula $F^{\bullet,B}$ +\end_inset + + is a type constructor with one type parameter (and with +\begin_inset Formula $B$ +\end_inset + + fixed). +\end_layout + +\begin_layout Standard +The type constructor \begin_inset Formula $F^{\bullet,B}$ \end_inset @@ -16579,12 +16692,16 @@ commutative diagram \end_inset + +\series bold commutative +\series default + \begin_inset Quotes erd \end_inset ). - In this way, the diagram illustrates at once the commutativity law + This diagram illustrates at once the law \begin_inset space ~ \end_inset @@ -17496,11 +17613,7 @@ noprefix "false" \end_layout \begin_layout Standard -The type constructor -\begin_inset Formula $\text{Id}^{A}\triangleq A$ -\end_inset - - is a lawful functor (the +The \series bold identity functor \series default @@ -17514,7 +17627,11 @@ identity functor \end_inset -). + +\begin_inset Formula $\text{Id}^{A}\triangleq A$ +\end_inset + + obeys the functor laws. \end_layout \begin_layout Subparagraph @@ -17770,6 +17887,11 @@ functor product \end_inset is also a functor. + We will write +\begin_inset Formula $P=L\times M$ +\end_inset + + for brevity. \end_layout \begin_layout Subparagraph @@ -18246,6 +18368,11 @@ fmap \end_inset . + We will write +\begin_inset Formula $L=P+Q$ +\end_inset + + for brevity. \end_layout \begin_layout Subparagraph @@ -19059,7 +19186,7 @@ map \end_inset - operation on the nested data type + operation on the composite data type \begin_inset listings inline true status open @@ -19334,10 +19461,10 @@ L^{A}\triangleq S^{A,L^{A}}\quad,\label{eq:f-def-recursive-functor} \end_inset where -\begin_inset Formula $S^{A,R}$ +\begin_inset Formula $S$ \end_inset - is a suitably chosen type constructor (called a + is a suitably chosen bifunctor called a \begin_inset Index idx status open @@ -19351,10 +19478,6 @@ recursion scheme|textit \series bold recursion scheme \series default -) with two type parameters -\begin_inset Formula $A,R$ -\end_inset - . If a recursion scheme \begin_inset Formula $S$ @@ -21262,7 +21385,7 @@ type equation : \begin_inset Formula \[ -L^{A}\triangleq S^{A,L^{A}}\triangleq\left(A\rightarrow\text{Int}\right)+L^{A}\times L^{A}\quad, +L^{A}\triangleq S^{A,L^{A}}=\left(A\rightarrow\text{Int}\right)+L^{A}\times L^{A}\quad, \] \end_inset @@ -21698,11 +21821,8 @@ Each time a type parameter is placed to the left of an \emph on uncurried \emph default - function arrow -\begin_inset Formula $\rightarrow$ -\end_inset - -, the variance is reversed: covariant becomes contravariant and vice versa. + function arrow, the variance is reversed: covariant becomes contravariant + and vice versa. For example: \begin_inset Formula \begin{align*} @@ -21809,7 +21929,7 @@ noprefix "false" \end_layout \begin_layout Itemize -Nested type constructors combine their variances: e.g., if +Composition of type constructors will combine their variances: e.g., if \begin_inset Formula $F^{A}$ \end_inset @@ -21834,7 +21954,7 @@ Nested type constructors combine their variances: e.g., if \end_inset . - This is shown in Exercises + See Exercise \begin_inset space ~ \end_inset @@ -21848,7 +21968,7 @@ noprefix "false" \end_inset -(c), (d). +(c),(d). \end_layout \begin_layout Standard @@ -21924,7 +22044,7 @@ To show that \begin_inset Formula $Z^{A,R}$ \end_inset - is indeed a functor in the parameter + is indeed covariant in \begin_inset Formula $A$ \end_inset @@ -21978,7 +22098,7 @@ map \end_inset function. - This would require a lot of work for a complicated type constructor such + That would require a lot of work for a complicated type constructor such as \begin_inset Formula $Z^{A,R}$ \end_inset @@ -22062,7 +22182,7 @@ Rewrite the Scala definitions in the type notation. \begin_inset Formula $G$ \end_inset - covariant or contravariant with respect to each type parameter? + covariant or contravariant with respect to their type parameters? \end_layout \begin_layout Standard @@ -22664,6 +22784,10 @@ def fmap_G[A, B, Z](f: A => B): G[A, Z] => G[B, Z] = { case G(p, q) => \end_inset + +\end_layout + +\begin_layout Standard In this way, the correct-by-construction code of \begin_inset Formula $\text{fmap}_{G}$ \end_inset @@ -22685,8 +22809,8 @@ derived \end_inset . - The corresponding algorithms could be implemented as a Scala macro library - that derives the code at compile time. + The corresponding algorithms could be even implemented as a Scala macro + library that derives the code at compile time. \end_layout \begin_layout Section @@ -22763,6 +22887,147 @@ Write more readable code using functor blocks to manipulate data wrapped in functors. \end_layout +\begin_layout Standard +What remains out of scope of those techniques? +\end_layout + +\begin_layout Itemize +Class definitions in Scala may use mutable values ( +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +var +\end_layout + +\end_inset + +). + For example: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +case class Q[A](var a: A, counter: Int) +\end_layout + +\end_inset + +Because of the presence of a mutable value +\begin_inset Index idx +status open + +\begin_layout Plain Layout +mutable value +\end_layout + +\end_inset + +, this type constructor is +\emph on +not +\emph default + properly expressed by the type formula +\begin_inset Formula $Q^{A}=A\times\text{Int}$ +\end_inset + +. + Mutable values ( +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +var +\end_layout + +\end_inset + +) are neither in a covariant nor in a contravariant position. + The functor and contrafunctor constructions do +\emph on +not +\emph default + apply to types that contain +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +var +\end_layout + +\end_inset + + definitions. +\end_layout + +\begin_layout Itemize +We have not talked about the type construction that imposes a universal + quantifier +\begin_inset Index idx +status open + +\begin_layout Plain Layout +universal quantifier +\end_layout + +\end_inset + + on a type parameter. + For example, if +\begin_inset Formula $S$ +\end_inset + + is a bifunctor then we may define a functor +\begin_inset Formula $F$ +\end_inset + + by +\begin_inset Formula $F^{A}\triangleq\forall B.\,S^{A,B}$ +\end_inset + +. + The corresponding code in Scala 2 is: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +// The bifunctor S[_, _] should be already defined. +\end_layout + +\begin_layout Plain Layout + +trait F[A]{ def s[B]: S[A, B] } +\end_layout + +\end_inset + +The syntax in Scala 3 is shorter: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +type F[A] = [B] => () => S[A, B] +\end_layout + +\end_inset + +This advanced construction is used less often. + We will develop tools for working with this construction in later chapters + of this book. +\end_layout + \begin_layout Subsection Exercises: Functor and contrafunctor constructions \begin_inset Index idx @@ -22800,15 +23065,15 @@ noprefix "false" \begin_layout Standard If -\begin_inset Formula $H^{A}$ +\begin_inset Formula $H$ \end_inset is a contrafunctor and -\begin_inset Formula $L^{A}$ +\begin_inset Formula $L$ \end_inset is a functor, show that -\begin_inset Formula $C\triangleq L^{A}\rightarrow H^{A}$ +\begin_inset Formula $C^{A}\triangleq L^{A}\rightarrow H^{A}$ \end_inset is a contrafunctor with the @@ -22831,12 +23096,12 @@ status open \begin_layout Plain Layout -def cmap[A, B](f: B => A)(c: L[A] => H[A]): L[B] => H[B] = { +def cmap_C[A, B](f: B => A)(c: L[A] => H[A]): L[B] => H[B] = \end_layout \begin_layout Plain Layout - lb: L[B] => cmap_H(f)(c(fmap_L(f)(lb))) // Code notation: ${ + (lb: L[B]) => cmap_H(f)(c(fmap_L(f)(lb))) // Code notation: ${ \backslash color{dkgreen} \backslash @@ -22863,11 +23128,6 @@ bef f^{ downarrow H}}$ \end_layout -\begin_layout Plain Layout - -} -\end_layout - \end_inset Here, @@ -22903,7 +23163,19 @@ fmap_L \end_inset . - Prove that the laws hold. + Prove that the laws hold for +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +cmap_C +\end_layout + +\end_inset + +. \end_layout \begin_layout Subsubsection @@ -22928,7 +23200,7 @@ noprefix "false" \end_layout \begin_layout Standard -Implement the required +Implement the \begin_inset listings inline true status open @@ -22940,7 +23212,7 @@ fmap \end_inset - or + or the \begin_inset listings inline true status open @@ -22979,11 +23251,11 @@ cmap \end_inset is a contrafunctor if -\begin_inset Formula $F^{A}$ +\begin_inset Formula $F$ \end_inset and -\begin_inset Formula $G^{A}$ +\begin_inset Formula $G$ \end_inset are contrafunctors. @@ -22999,11 +23271,11 @@ cmap \end_inset is a contrafunctor if -\begin_inset Formula $F^{A}$ +\begin_inset Formula $F$ \end_inset and -\begin_inset Formula $G^{A}$ +\begin_inset Formula $G$ \end_inset are contrafunctors. @@ -23471,7 +23743,10 @@ noprefix "false" \end_inset are also equivalent. - +\end_layout + +\begin_layout Standard + \series bold (b) \series default @@ -23584,7 +23859,7 @@ To see why, let us temporarily rename the contravariant occurrence of \end_inset The original type constructor -\begin_inset Formula $P^{A}$ +\begin_inset Formula $P$ \end_inset is expressed as @@ -23768,8 +24043,8 @@ P\text{'s composition law}:\quad & \text{xmap}_{P}(f_{1}^{:B\rightarrow A})(g_{1 \end_layout \begin_layout Standard -A type constructor -\begin_inset Formula $\tilde{P}^{Z,A}$ +This type constructor +\begin_inset Formula $\tilde{P}$ \end_inset with a lawful @@ -23788,7 +24063,11 @@ xmap \begin_inset Formula $_{\tilde{P}}$ \end_inset - is called a profunctor + is called a +\series bold +profunctor +\series default + \begin_inset Index idx status open @@ -23799,7 +24078,7 @@ profunctor \end_inset . - We will sometimes also call the type constructor + We will also call the type constructor \begin_inset Formula $P^{A}\triangleq\tilde{P}^{A,A}$ \end_inset @@ -23808,7 +24087,7 @@ profunctor \begin_layout Standard Consider an exponential-polynomial type constructor -\begin_inset Formula $P^{A}$ +\begin_inset Formula $P$ \end_inset such as: @@ -23823,8 +24102,8 @@ Each copy of the type parameter \begin_inset Formula $A$ \end_inset - will occur either in covariant or in a contravariant position because no - other possibility is available in exponential-polynomial types. + will occur either in covariant or in a contravariant position, because + no other possibility is available in exponential-polynomial types. So, we can always rename all contravariant occurrences of the type parameter \begin_inset Formula $A$ @@ -23926,7 +24205,7 @@ status open \begin_layout Plain Layout -ServerAction[R] +ServerAction \end_layout \end_inset @@ -23965,12 +24244,12 @@ status open \begin_layout Plain Layout -ServerAction[R] +ServerAction \end_layout \end_inset - because it is not a fully parametric type constructor (and so is not exponentia + because it is not a fully parametric type constructor (and it is not exponentia l-polynomial). \end_layout @@ -24761,7 +25040,7 @@ noprefix "false" \begin_layout Standard If -\begin_inset Formula $L^{A}$ +\begin_inset Formula $L$ \end_inset is a lawful functor and diff --git a/sofp-src/lyx/sofp-induction.lyx b/sofp-src/lyx/sofp-induction.lyx index 11d1a99e0..1f5feaf39 100644 --- a/sofp-src/lyx/sofp-induction.lyx +++ b/sofp-src/lyx/sofp-induction.lyx @@ -27680,7 +27680,7 @@ mutable status open \begin_layout Plain Layout -mutability +mutable value \end_layout \end_inset diff --git a/sofp-src/lyx/sofp-monads.lyx b/sofp-src/lyx/sofp-monads.lyx index e1c4bd2f9..8b47b3762 100644 --- a/sofp-src/lyx/sofp-monads.lyx +++ b/sofp-src/lyx/sofp-monads.lyx @@ -2120,7 +2120,27 @@ aab \end_inset . - To achieve that, we must make + We use the inline +\begin_inset Quotes eld +\end_inset + + +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +if +\end_layout + +\end_inset + + +\begin_inset Quotes erd +\end_inset + + syntax to let \begin_inset listings inline true status open @@ -2132,7 +2152,7 @@ y \end_inset - iterate over letters that do not include the current value of + iterate over letters different from the previously chosen values of \begin_inset listings inline true status open @@ -2144,14 +2164,50 @@ x \end_inset -: +, and to let +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +z +\end_layout + +\end_inset + + iterate over letters different from the previously chosen +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +x +\end_layout + +\end_inset + + and +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +y +\end_layout + +\end_inset + +: \begin_inset listings inline false status open \begin_layout Plain Layout -val xs = Seq( +val letters = Seq( \begin_inset Quotes eld \end_inset @@ -2189,27 +2245,27 @@ scala> for { \begin_layout Plain Layout - x <- xs + x <- letters \end_layout \begin_layout Plain Layout - xsWithoutX = xs.filter(_ != x) + y <- letters \end_layout \begin_layout Plain Layout - y <- xsWithoutX + if y != x \end_layout \begin_layout Plain Layout - xsWithoutXY = xsWithoutX.filter(_ != y) + z <- letters \end_layout \begin_layout Plain Layout - z <- xsWithoutXY + if z != x && z != y \end_layout \begin_layout Plain Layout @@ -22034,8 +22090,23 @@ abstract class Monad[F[_]: Functor : Semi-monad] { \begin_layout Plain Layout -def checkMonadIdentityLaws[F[_], A, B]()(implicit mf: Monad[F], sf: Semi-monad[F -], +object Monad { // For convenience: enables Monad[F].pure() syntax. +\end_layout + +\begin_layout Plain Layout + + def apply[F[_]: Monad]: Monad[F] = implicitly[Monad[F]] +\end_layout + +\begin_layout Plain Layout + +} +\end_layout + +\begin_layout Plain Layout + +def checkMonadIdentityLaws[F[_], A, B]()(implicit mf: Monad[F], sf: Semimonad[F] +, \end_layout \begin_layout Plain Layout @@ -26949,11 +27020,23 @@ As with other monads of function type, derivations are made shorter by using \begin_inset Formula $F$ \end_inset - is a monad, we also assume that its pure method + is a monad, we also assume that its +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +pure +\end_layout + +\end_inset + + method ( \begin_inset Formula $\text{pu}_{F}$ \end_inset - is known and obeys the identity laws. +) is known and obeys the identity laws. For the monad \begin_inset Formula $L$ \end_inset @@ -27184,7 +27267,7 @@ def flatMap_L[A, B](la: L[A])(f: A => L[B]): L[B] = { z => la(z).flatMap(a \begin_layout Plain Layout -def pure_L[A](a: A): L[A] = { _ => implicitly[Monad[F]].pure(a) } +def pure_L[A](a: A): L[A] = { _ => Monad[F].pure(a) } \end_layout \end_inset @@ -29738,7 +29821,7 @@ flatten \end_inset - is written in the code notation as: + is written as: \begin_inset Index idx status open @@ -29781,7 +29864,7 @@ cannot \end_inset . - Find a counterexample with a specific + (Find a counterexample with a specific \begin_inset Formula $S$ \end_inset @@ -29801,7 +29884,7 @@ cannot \begin_inset Formula $g^{\uparrow S}\bef\text{ftn}_{S}\neq\text{ftn}_{S}\bef g$ \end_inset -. +.) \end_layout \begin_layout Subsubsection @@ -29845,6 +29928,18 @@ status open \begin_layout Plain Layout +pure +\end_layout + +\end_inset + + method remains unchanged but the +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + flatten \end_layout @@ -29871,8 +29966,12 @@ status open \begin_layout Plain Layout -def flatten[A](p: List[List[A]]): List[A] = if (p.exists(_.isEmpty)) Nil else - p.flatten +def flatten[A](p: List[List[A]]): List[A] = +\end_layout + +\begin_layout Plain Layout + + if (p.exists(_.isEmpty)) Nil else p.flatten \end_layout \end_inset @@ -30032,7 +30131,7 @@ Given an arbitrary monad \begin_inset Formula $M^{A}+M^{A}$ \end_inset -) is a semi-monad but not a full monad. +) is a semi-monad but not always a monad. \end_layout \begin_layout Subsubsection @@ -30061,7 +30160,7 @@ Show that \begin_inset Formula $P^{A}\triangleq Z+W\times A$ \end_inset - is a (full) monad if + is a monad if \begin_inset Formula $W$ \end_inset @@ -31152,17 +31251,25 @@ Use monad constructions to show that the functors \begin_inset Formula $p(x)=x^{n_{1}}+x^{n_{2}}+...+x^{n_{k}}$ \end_inset - with some distinct positive integers -\begin_inset Formula $n_{1}<... x} +\end_layout + +\end_inset + + to \begin_inset listings inline true status open \begin_layout Plain Layout -{ z => z } +{z => z} \end_layout \end_inset @@ -1869,12 +1882,12 @@ val t: S = R(30) // Another value of type S. \end_inset -Code notation: +Code notation (with all types annotated for clarity): \begin_inset Formula \begin{align*} S & \triangleq\text{Int}\times\text{Int}+\text{String}+\text{Int}\quad,\\ -s^{:S} & \triangleq10\times20+\bbnum 0^{:\text{String}}+\bbnum 0^{:\text{Int}}\quad,\\ -t^{:S} & \triangleq\bbnum 0^{:\text{Int}\times\text{Int}}+\bbnum 0^{:\text{String}}+30\quad. +s^{:S} & \triangleq(10\times20)^{:\text{Int}\times\text{Int}}+\bbnum 0^{:\text{String}}+\bbnum 0^{:\text{Int}}\quad,\\ +t^{:S} & \triangleq\bbnum 0^{:\text{Int}\times\text{Int}}+\bbnum 0^{:\text{String}}+30^{:\text{Int}}\quad. \end{align*} \end_inset @@ -1901,7 +1914,7 @@ R(x) . The advantage is that we may explicitly annotate all types and show clearly - the part of the disjunction that we are creating. + the part of the disjunctive type we are creating. Another advantage is that the notation \begin_inset Formula $\bbnum 0+\bbnum 0+x$ \end_inset @@ -2438,15 +2451,22 @@ void type!in matrix notation \begin_inset Formula $\bbnum 0$ \end_inset -) is written in the first column to indicate that the disjunctive part in - that column is not returned. - There is no confusion with other columns because the type +) is written in the first column to indicate that the disjunctive part ( +\begin_inset Formula $\bbnum 1+\bbnum 0^{:\text{Int}}$ +\end_inset + +) corresponding to that column is not returned. + There is no confusion in using the symbol +\begin_inset Formula $\bbnum 0$ +\end_inset + + because the void type \begin_inset Formula $\bbnum 0$ \end_inset has no values. - In this way, the matrix clearly displays the parts of disjunctive types - that are being returned in each case. + The matrix clearly displays the parts of disjunctive types that are being + returned in each case. \end_layout \begin_layout Standard @@ -2611,7 +2631,7 @@ row vector Calculations use the standard rules of a vector-matrix product: \begin_inset Formula \begin{align*} - & (\bbnum 0+64)\triangleright\,\begin{array}{||cc|} + & (\bbnum 0^{:\bbnum 1}+64^{:\text{Int}})\triangleright\,\begin{array}{||cc|} \bbnum 0 & \_\rightarrow100\\ \bbnum 0 & x\rightarrow\frac{x}{2} \end{array}\,=\,\begin{array}{|cc|} @@ -2862,7 +2882,7 @@ argument-free \end_inset ). - Many laws can be formulated and used more easily in the point-free form. + Many laws can be formulated and used more easily in that style. \end_layout @@ -3123,7 +3143,7 @@ How can we verify this and other similar computations where the operations substituted as parameters into functions, for example: \begin_inset Formula \begin{align*} -\text{substitute }x\text{ instead of }a:\quad & \gunderline x\triangleright(\gunderline a\rightarrow f(\gunderline a))=f(x)\quad.\\ +\text{substitute }x\text{ instead of }a:\quad & \gunderline x\triangleright(\gunderline a\rightarrow f(\gunderline a))=f(x)\quad,\\ \text{substitute }f(x)\text{ instead of }y:\quad & (x\rightarrow\gunderline{f(x)})\bef(\gunderline y\rightarrow g(\gunderline y))=x\rightarrow g(f(x))\quad. \end{align*} @@ -4516,7 +4536,7 @@ In some cases, we cannot fully split the rows or the columns of a matrix. \begin_inset Formula $2\times2$ \end_inset - matrix because we do not know which parts of the disjunction are returned + matrix because we do not know which parts of the disjunctive type are returned (the code of the function \begin_inset Formula $f$ \end_inset @@ -4807,8 +4827,8 @@ B & \Delta\bef(f_{2}\boxtimes g_{2}) \end_inset The rules of matrix multiplication do not help in deriving this law directly. - So, we use a more basic approach: show that both sides are equal when applied - to arbitrary values + So, we use a more basic approach and show that both sides are equal when + applied to arbitrary values \begin_inset Formula $p$ \end_inset @@ -4983,7 +5003,7 @@ At this point, the only things we can simplify are the identity functions \end_inset . - So, we continue the derivation, omitting types: + So, we continue the derivation: \begin_inset Formula \begin{align*} \text{expect to equal }\text{id}:\quad & \text{id}^{\uparrow L}=a\times p\rightarrow\gunderline{\text{id}\,(a)}\times(p\triangleright\gunderline{\text{id}^{\uparrow F}})\\ diff --git a/sofp-src/lyx/sofp-summary.lyx b/sofp-src/lyx/sofp-summary.lyx index 221bf7535..7e9af90a8 100644 --- a/sofp-src/lyx/sofp-summary.lyx +++ b/sofp-src/lyx/sofp-summary.lyx @@ -1617,83 +1617,8 @@ noprefix "false" \end_layout \begin_layout Standard -Find the smallest integer expressible as a sum of two cubed integers in - more than one way. -\end_layout - -\begin_layout Subsubsection -Exercise -\begin_inset CommandInset label -LatexCommand label -name "par:Exercise-additional-18" - -\end_inset - - -\begin_inset CommandInset ref -LatexCommand ref -reference "par:Exercise-additional-18" -plural "false" -caps "false" -noprefix "false" - -\end_inset - - -\end_layout - -\begin_layout Standard -Show that the following type signatures have -\emph on -no -\emph default - fully parametric implementations: -\end_layout - -\begin_layout Standard -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -def f[A]: Option[A] => A -\end_layout - -\begin_layout Plain Layout - -def g[A, B]: (A => B) => A -\end_layout - -\begin_layout Plain Layout - -def h[A, B]: (A => B) => (B => A) -\end_layout - -\begin_layout Plain Layout - -def k[A, B, C]: (A => B) => (B => C) => (C => A) -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Standard -Hint: set some type parameters to the void type ( -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -Nothing -\end_layout - -\end_inset - -). +Write a function for finding the smallest integer expressible as a sum of + two cubed integers in more than one way. \end_layout \begin_layout Subsubsection @@ -2156,10 +2081,11 @@ noprefix "false" \end_inset -\end_layout +\begin_inset Foot +status collapsed -\begin_layout Standard -(Exercise 4-1 from Hu Zhenjiang's course +\begin_layout Plain Layout +Exercise 4-1 from Hu Zhenjiang's course \family typewriter \begin_inset CommandInset href @@ -2171,7 +2097,16 @@ literal "false" \family default -) Express the +. +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Express the \begin_inset listings inline true status open @@ -2331,12 +2266,8 @@ noprefix "false" \end_inset -\end_layout - -\begin_layout Standard -Find \begin_inset Foot -status collapsed +status open \begin_layout Plain Layout This was posted in @@ -2354,7 +2285,11 @@ literal "false" \end_inset - a general type signature for the expression + +\end_layout + +\begin_layout Standard +Find a general type signature for the expression \begin_inset Formula $a\rightarrow a(y\rightarrow t\rightarrow t)(z(a))$ \end_inset @@ -2381,11 +2316,32 @@ noprefix "false" \end_inset +\begin_inset Foot +status open + +\begin_layout Plain Layout +R. +\begin_inset space ~ +\end_inset + +Bird and O. +\begin_inset space ~ +\end_inset + +de Moor, +\emph on +Algebra of programming +\emph default + (1996), page 20. +\end_layout + +\end_inset + + \end_layout \begin_layout Standard -(Bird-de Moor, page 20) Derive the following identity between functions - +Derive the following identity between functions \begin_inset Formula $F^{A}\rightarrow F^{A}$ \end_inset @@ -2517,7 +2473,7 @@ C & \bbnum 0 & \text{id} \end_inset Show that all polynomial functors -\begin_inset Formula $F^{\bullet}$ +\begin_inset Formula $F$ \end_inset belong to this typeclass. @@ -2643,6 +2599,245 @@ Given a monad Hint: use the flipped Kleisli technique. \end_layout +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "par:Exercise-additional-7-1" + +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "par:Exercise-additional-7-1" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + +\begin_inset Foot +status open + +\begin_layout Plain Layout +See +\family typewriter + +\begin_inset CommandInset href +LatexCommand href +target "https://cstheory.stackexchange.com/questions/54227" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +A monad +\begin_inset Formula $M$ +\end_inset + +'s +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +flatMap +\end_layout + +\end_inset + + method obeys the naturality law: +\begin_inset Formula +\[ +\text{flm}_{M}(g\bef h^{\uparrow M})=\text{flm}_{M}(g)\bef h^{\uparrow M}\quad. +\] + +\end_inset + +This law holds with an arbitrary function +\begin_inset Formula $h:A\rightarrow B$ +\end_inset + +. + Show that one cannot replace +\begin_inset Formula $h^{\uparrow M}$ +\end_inset + + in this law by an arbitrary function +\begin_inset Formula $k:M^{A}\rightarrow M^{B}$ +\end_inset + +. + Namely, for some monads +\begin_inset Formula $M$ +\end_inset + + and some functions +\begin_inset Formula $g$ +\end_inset + +, +\begin_inset Formula $k$ +\end_inset + + (of appropriate types) we will have +\begin_inset Formula $\text{flm}_{M}(g\bef k)\neq\text{flm}_{M}(g)\bef k$ +\end_inset + +. +\end_layout + +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "par:Exercise-additional-18-1" + +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "par:Exercise-additional-18-1" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + +\end_layout + +\begin_layout Standard +Find fully parametric implementations of the type signatures: +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +def a[A, B, C]: ((C => B) => A) => B => A +\end_layout + +\begin_layout Plain Layout + +def b[A, B]: (((A => B) => B) => B) => A => B +\end_layout + +\begin_layout Plain Layout + +def c[A, B]: ((((A => B) => A) => A) => B) => B +\end_layout + +\begin_layout Plain Layout + +def d[A, B, C]: (((A => B) => C) => A => B) => (B => C) => A => B +\end_layout + +\begin_layout Plain Layout + +def e[A, B, C]: ((B => C) => A => B) => ((A => B) => C) => A => B +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "par:Exercise-additional-18" + +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "par:Exercise-additional-18" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + +\end_layout + +\begin_layout Standard +Show that the following type signatures have +\emph on +no +\emph default + fully parametric implementations: +\end_layout + +\begin_layout Standard +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +def f[A]: Option[A] => A +\end_layout + +\begin_layout Plain Layout + +def g[A, B]: (A => B) => A +\end_layout + +\begin_layout Plain Layout + +def h[A, B]: (A => B) => B => A +\end_layout + +\begin_layout Plain Layout + +def k[A, B, C]: (A => B) => (B => C) => C => A +\end_layout + +\begin_layout Plain Layout + +def l[A, B]: ((((A => B) => B) => A) => B) => B +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +Hint: set some type parameters to the void type ( +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Nothing +\end_layout + +\end_inset + +). +\end_layout + \begin_layout Subsubsection Exercise \begin_inset CommandInset label @@ -2683,11 +2878,11 @@ Given two fixed types . Show that there exist natural transformations -\begin_inset Formula $F^{\bullet}\leadsto G^{\bullet}$ +\begin_inset Formula $F\leadsto G$ \end_inset and -\begin_inset Formula $G^{\bullet}\leadsto F^{\bullet}$ +\begin_inset Formula $G\leadsto F$ \end_inset . @@ -2741,11 +2936,8 @@ Given two fixed types \begin_inset Formula $L$ \end_inset - is also not a full monad because -\begin_inset Formula $L$ -\end_inset - - is then equivalent to a composition of the continuation monad with itself. + is then equivalent to a composition of the continuation monad with itself; + that is also only a semimonad. See Exercise \begin_inset space ~ \end_inset @@ -2786,7 +2978,7 @@ noprefix "false" \begin_layout Standard For any fully parametric contrafunctor -\begin_inset Formula $F^{A}$ +\begin_inset Formula $F$ \end_inset that does not explicitly use the void type @@ -2835,15 +3027,15 @@ The type equivalence \end_inset . - This agrees with the intuition that a contrafunctor type ( + This agrees with the intuition that value of type \begin_inset Formula $F^{A}$ \end_inset -) + \begin_inset Quotes eld \end_inset -does not contain +do not store \begin_inset Quotes erd \end_inset @@ -2909,7 +3101,7 @@ If \end_inset is any monad then -\begin_inset Formula $M^{A+M^{A}}$ +\begin_inset Formula $L^{A}\triangleq M^{A+M^{A}}$ \end_inset is also a lawful monad. @@ -3008,7 +3200,7 @@ noprefix "false" \begin_layout Standard If -\begin_inset Formula $M^{\bullet}$ +\begin_inset Formula $M$ \end_inset is a commutative monad and @@ -3231,48 +3423,43 @@ noprefix "false" \end_layout \begin_layout Standard -Use the Curry-Howard correspondence -\begin_inset Index idx +Simplify the type +\begin_inset Formula $\forall A.\,((A\rightarrow A)\rightarrow A)\rightarrow A$ +\end_inset + +, or in Scala: +\begin_inset listings +inline false status open \begin_layout Plain Layout -Curry-Howard correspondence + +def f[A]: ((A => A) => A) => A \end_layout \end_inset - and the algorithms LJ or LJT -\begin_inset Index idx +into a type expression that contains no quantifiers. +\begin_inset Foot status open \begin_layout Plain Layout -LJT algorithm -\end_layout +See +\family typewriter -\end_inset +\begin_inset CommandInset href +LatexCommand href +target "https://cstheory.stackexchange.com/questions/53855/" +literal "false" - to prove that there exists only one fully parametric function with type - signature -\begin_inset Formula $\forall A.\,((A\rightarrow A)\rightarrow A)\rightarrow A$ \end_inset -, or in Scala: -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout -def f[A]: ((A => A) => A) => A \end_layout \end_inset -From that, prove the type equivalence -\begin_inset Formula $\forall A.\,((A\rightarrow A)\rightarrow A)\rightarrow A\cong\bbnum 1$ -\end_inset -. \end_layout \begin_layout Subsubsection @@ -3298,7 +3485,7 @@ noprefix "false" \begin_layout Standard Consider the functor -\begin_inset Formula $F^{R}$ +\begin_inset Formula $F$ \end_inset defined by: @@ -3387,7 +3574,7 @@ noprefix "false" \begin_layout Standard Consider the profunctor -\begin_inset Formula $F^{R,S}$ +\begin_inset Formula $F$ \end_inset defined by: @@ -3635,20 +3822,16 @@ noprefix "false" \end_layout \begin_layout Standard -Prove the following type equivalences (assuming fixed types +Prove the following type equivalences (assuming a fixed type \begin_inset Formula $P$ \end_inset -, -\begin_inset Formula $Q$ -\end_inset - -, ...): +): \end_layout \begin_layout Standard \begin_inset Tabular - + @@ -3751,32 +3934,6 @@ Equivalent type \end_inset -\end_layout - -\end_inset - - - - -\begin_inset Text - -\begin_layout Plain Layout -\begin_inset Formula $\forall A.\,(A\rightarrow A)\rightarrow A\rightarrow A$ -\end_inset - - -\end_layout - -\end_inset - - -\begin_inset Text - -\begin_layout Plain Layout -\begin_inset Formula $\text{List}^{\bbnum 1}$ -\end_inset - - \end_layout \end_inset @@ -3802,7 +3959,7 @@ Equivalent type \begin_inset Formula $P$ \end_inset -? + \end_layout \end_inset @@ -3838,7 +3995,7 @@ noprefix "false" \begin_layout Standard Consider the type constructor -\begin_inset Formula $F^{R,S}$ +\begin_inset Formula $F$ \end_inset defined by: @@ -4009,7 +4166,15 @@ literal "false" \end_inset ) is equivalent to -\begin_inset Formula $\text{List}^{\bullet}$ +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +List +\end_layout + \end_inset via monad morphisms. @@ -4085,7 +4250,7 @@ noprefix "false" defined by: \begin_inset Formula \[ -U^{M^{\bullet},A}\triangleq\forall R.\,(A\rightarrow G^{M^{R}})\rightarrow G^{M^{R}}=\forall R.\,(A\rightarrow M^{R}\rightarrow M^{R})\rightarrow M^{R}\rightarrow M^{R} +U^{M,A}\triangleq\forall R.\,(A\rightarrow G^{M^{R}})\rightarrow G^{M^{R}}=\forall R.\,(A\rightarrow M^{R}\rightarrow M^{R})\rightarrow M^{R}\rightarrow M^{R} \] \end_inset @@ -4185,17 +4350,17 @@ literal "false" : \begin_inset Formula \[ -V^{F^{\bullet},P,Q,M^{\bullet},A}\triangleq\forall R.\,(A\rightarrow F^{M^{R}}\rightarrow P\times M^{R}+Q)\rightarrow F^{M^{R}}\rightarrow P\times M^{R}+Q\quad. +V^{F,P,Q,M,A}\triangleq\forall R.\,(A\rightarrow F^{M^{R}}\rightarrow P\times M^{R}+Q)\rightarrow F^{M^{R}}\rightarrow P\times M^{R}+Q\quad. \] \end_inset Show that there exists a monad morphism -\begin_inset Formula $M^{A}\rightarrow V^{F^{\bullet},P,Q,M^{\bullet},A}$ +\begin_inset Formula $M^{A}\rightarrow V^{F,P,Q,M,A}$ \end_inset , and that the converse function of type -\begin_inset Formula $V^{F^{\bullet},P,Q,M^{\bullet},A}\rightarrow M^{A}$ +\begin_inset Formula $V^{F,P,Q,M,A}\rightarrow M^{A}$ \end_inset exists when @@ -4255,7 +4420,7 @@ FMT : \begin_inset Formula \[ -\text{FMT}^{M^{\bullet},A}\triangleq\forall X^{:\text{Monoid}}.\,(A\rightarrow M^{X})\rightarrow M^{X}\quad, +\text{FMT}^{M,A}\triangleq\forall X^{:\text{Monoid}}.\,(A\rightarrow M^{X})\rightarrow M^{X}\quad, \] \end_inset @@ -4670,12 +4835,12 @@ not \end_inset with some applicative functor -\begin_inset Formula $G^{\bullet}$ +\begin_inset Formula $G$ \end_inset ? \begin_inset Note Note -status open +status collapsed \begin_layout Subsubsection Problem @@ -5164,7 +5329,7 @@ Are there any monadically natural monad morphisms that are not identity functions? (Equivalently, any non-identical natural transformations -\begin_inset Formula $\text{Id}^{\bullet}\leadsto\text{Id}^{\bullet}$ +\begin_inset Formula $\text{Id}\leadsto\text{Id}$ \end_inset between identity functors in the category of monads?) If it were possible @@ -5263,6 +5428,8 @@ literal "false" \end_inset +\family default + for a solution. \end_layout \end_inset @@ -5971,7 +6138,7 @@ This is suspicious: we need to show that an expression \end_layout \begin_layout Plain Layout -Note that +Note that: \begin_inset Formula \[ \text{pu}_{M}(r^{:R})\oplus_{M}q^{:M^{R}}=r\triangleright\gunderline{\text{pu}_{M}\triangleright\text{flm}_{M}}(u\rightarrow q\triangleright(v\rightarrow u\oplus_{R}v)^{\uparrow M})=q\triangleright(v\rightarrow r\oplus_{R}v)^{\uparrow M}\quad. @@ -6161,12 +6328,8 @@ noprefix "false" \end_layout \begin_layout Standard -For any fully parametric type constructor -\begin_inset Formula $P^{A}$ -\end_inset - - covariant in -\begin_inset Formula $A$ +For any fully parametric and covariant type constructor +\begin_inset Formula $P$ \end_inset , the lifting of a function diff --git a/sofp-src/lyx/sofp-transformers.lyx b/sofp-src/lyx/sofp-transformers.lyx index 653111ebc..1ba530924 100644 --- a/sofp-src/lyx/sofp-transformers.lyx +++ b/sofp-src/lyx/sofp-transformers.lyx @@ -19312,7 +19312,7 @@ In total, we found 18 laws for monad transformers. \end_layout \begin_layout Standard -The main use of the laws is to verify correctness of the code. +The main use of the laws is to assure correctness of the code. The next section shows some examples of incorrect implementations of monad transformers and indicates the laws that are violated. \end_layout @@ -19743,7 +19743,7 @@ Functor co-product \begin_layout Standard The co-product -\begin_inset Formula $L^{\bullet}+M^{\bullet}$ +\begin_inset Formula $L+M$ \end_inset is in general not a monad when @@ -19795,7 +19795,7 @@ noprefix "false" ). But even when -\begin_inset Formula $L^{\bullet}+M^{\bullet}$ +\begin_inset Formula $L+M$ \end_inset is a monad, the identity law is violated: with @@ -19819,29 +19819,29 @@ Using the free monad \begin_layout Standard The functor composition -\begin_inset Formula $L^{M^{\bullet}}$ +\begin_inset Formula $L\circ M$ \end_inset and the co-product -\begin_inset Formula $L^{\bullet}+M^{\bullet}$ +\begin_inset Formula $L+M$ \end_inset may not always be monads, but they are always functors. We can make monads out of those functors via the free monad construction. We obtain -\begin_inset Formula $\text{Free}^{L^{M^{\bullet}}}$ +\begin_inset Formula $\text{Free}^{L\circ M}$ \end_inset , the free monad on -\begin_inset Formula $L^{M^{\bullet}}$ +\begin_inset Formula $L\circ M$ \end_inset , and -\begin_inset Formula $\text{Free}^{L^{\bullet}+M^{\bullet}}$ +\begin_inset Formula $\text{Free}^{L+M}$ \end_inset -, the free monad on -\begin_inset Formula $L^{\bullet}+M^{\bullet}$ +, the free monad on the functor co-product +\begin_inset Formula $L+M$ \end_inset . @@ -19849,13 +19849,13 @@ The functor composition However, the identity laws fail: \begin_inset Formula \[ -\text{Free}^{L^{\text{Id}^{\bullet}}}\cong\text{Free}^{L^{\bullet}}\not\cong L\quad,\quad\quad\text{Free}^{L^{\bullet}+\text{Id}^{\bullet}}\not\cong L\quad. +\text{Free}^{L\circ\text{Id}}\cong\text{Free}^{L}\not\cong L\quad,\quad\quad\text{Free}^{L+\text{Id}}\not\cong L\quad. \] \end_inset The lifting laws are also violated because -\begin_inset Formula $\text{flift}:M^{A}\rightarrow\text{Free}^{L^{\bullet}+M^{\bullet},A}$ +\begin_inset Formula $\text{flift}:M^{A}\rightarrow\text{Free}^{L+M,A}$ \end_inset is not a monad morphism (it maps @@ -20378,7 +20378,7 @@ can be implemented only as a function that returns a constant unit value \end_inset and -\begin_inset Formula $M^{\bullet}$ +\begin_inset Formula $M$ \end_inset yields the following type constructor: @@ -20390,7 +20390,7 @@ F^{A}\triangleq\forall B.\,(M^{A}\rightarrow L^{B})\rightarrow L^{B}\quad. \end_inset However, -\begin_inset Formula $F^{A}$ +\begin_inset Formula $F$ \end_inset fails to be a monad for all @@ -29341,7 +29341,7 @@ and \begin_inset Formula $T_{R_{2}}^{M}=R_{2}\circ M$ \end_inset -, the stack of transformers is expressed as +, the stack of transformers is expressed as: \begin_inset Formula \[ T_{R_{1}}^{T_{R_{2}}^{M}}=R_{1}\circ T_{R_{2}}^{M}=R_{1}\circ(R_{2}\circ M)=R_{1}\circ R_{2}\circ M\quad. diff --git a/sofp-src/lyx/sofp-traversable.lyx b/sofp-src/lyx/sofp-traversable.lyx index 96cb38fdc..23f2e518b 100644 --- a/sofp-src/lyx/sofp-traversable.lyx +++ b/sofp-src/lyx/sofp-traversable.lyx @@ -424,7 +424,7 @@ zip \end_inset ) and generalized them to many different type constructors. - This chapter adopts the same approach to study and generalize the + This chapter adopts the same approach to examine and generalize the \begin_inset listings inline true status open @@ -436,8 +436,16 @@ reduce \end_inset - method. - In this way, we will conclude the study of the + method, which will lead to the concept of +\begin_inset Quotes eld +\end_inset + +traversable functors +\begin_inset Quotes erd +\end_inset + +. + In this way, we will complete a theoretical study of the \begin_inset listings inline true status open @@ -10328,7 +10336,34 @@ fold(f)(x) \end_layout \begin_layout Standard -Instead of working with the general function +The method +\begin_inset Formula $\text{fold}_{S}$ +\end_inset + + works in the same way for all recursion schemes +\begin_inset Formula $S$ +\end_inset + +. + This makes +\begin_inset Formula $\text{fold}_{S}$ +\end_inset + + powerful but hard to use because we need to work with data structures defined + via the +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Fix +\end_layout + +\end_inset + + type constructor. + Instead of working with the general function \begin_inset Formula $\text{fold}_{S}$ \end_inset @@ -10344,11 +10379,11 @@ Fix \end_inset -, it is more convenient to implement and use specialized versions of +, it is more convenient to implement and use a specialized code of \begin_inset Formula $\text{fold}_{S}$ \end_inset - for already defined recursive types. + adapted to the data structure at hand. The general implementation of \begin_inset Formula $\text{fold}_{S}$ \end_inset @@ -10367,8 +10402,9 @@ noprefix "false" \end_inset -) can be translated mechanically (e.g., using macros or code generators) into - code specialized for a given data type and recursion scheme. +) can be translated mechanically (in principle, even automatically using + macros or code generators) into code specialized for a given data type + and recursion scheme. \end_layout \begin_layout Standard @@ -10538,9 +10574,9 @@ Tree" + foldTreeN[A, String](toLaTeX3)(tree) \end_layout \begin_layout Standard -Another simple example of an aggregation operation that cannot be expressed - as a traversal is the task of determining the maximum branching number - of a given rose tree. +An example of an aggregation operation that cannot be expressed as a traversal + is the task of determining the maximum branching number of a given rose + tree. The function \begin_inset listings inline true @@ -10602,133 +10638,111 @@ res2: Int = 2 \end_layout -\begin_layout Subsection -Recursion schemes. - II. - Unfolding operations -\end_layout - \begin_layout Standard -A folding operation converts a collection to a single value. - The opposite operation is -\begin_inset Quotes eld -\end_inset - -unfolding -\begin_inset Quotes erd -\end_inset +Another example of an operation not expressible via +\begin_inset listings +inline true +status open -: converting a single value into a collection. - By reversing the direction of certain function arrows in the type signature - of -\begin_inset Formula $\text{fold}_{S}$ -\end_inset +\begin_layout Plain Layout -, we can define a general -\begin_inset Quotes eld -\end_inset +traverse +\end_layout -unfolding -\begin_inset Quotes erd \end_inset - method that uses an arbitrary recursion scheme -\begin_inset Formula $S$ -\end_inset + is +\begin_inset listings +inline true +status open - and an arbitrary function of type -\begin_inset Formula $Z\rightarrow S^{A,Z}$ -\end_inset +\begin_layout Plain Layout -: -\begin_inset Formula -\begin{equation} -\text{unfold}_{S}:(Z\rightarrow S^{A,Z})\rightarrow Z\rightarrow L^{A}\quad,\quad\text{unfold}_{S}(f)\triangleq f\bef\overline{\text{unfold}_{S}(f)}^{\uparrow S^{A,\bullet}}\quad.\label{eq:unfold-via-recursion-scheme} -\end{equation} +zipWithDepth +\end_layout \end_inset -Section +, which we implemented in Section \begin_inset space ~ \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "sec:ch2Converting-a-single" +reference "subsec:Tasks-not-implementable-via-traverse" plural "false" caps "false" noprefix "false" \end_inset - showed an unfolding operation for sequences: starting from an initial value, - a function is applied repeatedly to compute further elements of the sequence. - The + through custom code. + We will now implement \begin_inset listings inline true status open \begin_layout Plain Layout -unfold +zipWithDepth \end_layout \end_inset - operation generalizes that computation to an arbitrary recursive type construct -or -\begin_inset Formula $L$ + via +\begin_inset Formula $\text{fold}_{S}$ \end_inset - whose recursion scheme -\begin_inset Formula $S$ + specialized to the binary tree recursion scheme +\begin_inset Formula $S^{A,R}\triangleq A+R\times R$ \end_inset - is given. - -\end_layout - -\begin_layout Standard -To get more intuition, we look at some examples using + that defines the tree type constructor \begin_inset listings inline true status open \begin_layout Plain Layout -unfold +T2 \end_layout \end_inset - with lists and binary trees. -\end_layout +. + The code for the specialized version of +\begin_inset Formula $\text{fold}_{S}$ +\end_inset -\begin_layout Subsubsection -Example -\begin_inset CommandInset label -LatexCommand label -name "subsec:Example-unfold-list" + is: +\begin_inset listings +inline false +status open -\end_inset +\begin_layout Plain Layout + +type S[A, R] = Either[A, (R, R)] // Recursion scheme for T2[A]. +\end_layout +\begin_layout Plain Layout -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Example-unfold-list" -plural "false" -caps "false" -noprefix "false" +def foldT2[A, Z](f: S[A, Z] => Z): T2[A] => Z = { +\end_layout -\end_inset +\begin_layout Plain Layout + case Leaf(a) => f(Left(a)) +\end_layout -\begin_inset Index idx -status open +\begin_layout Plain Layout + + case Branch(l, r) => f(Right((foldT2(f)(l), foldT2(f)(r)))) +\end_layout \begin_layout Plain Layout -examples (with code) + +} \end_layout \end_inset @@ -10737,174 +10751,173 @@ examples (with code) \end_layout \begin_layout Standard -Use +The function call \begin_inset listings inline true status open \begin_layout Plain Layout -unfold +foldT2(f) \end_layout \end_inset - to create a + will be able to convert any tree into a value of type \begin_inset listings inline true status open \begin_layout Plain Layout -List +Z \end_layout \end_inset - of consecutive powers of -\begin_inset Formula $2$ -\end_inset - - up to a given value -\begin_inset Formula $n$ -\end_inset - -: + if we supply a function \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -type S[A, R] = ??? +f \end_layout -\begin_layout Plain Layout +\end_inset -type Z = ??? -\end_layout + of type +\begin_inset Formula $A+Z\times Z\rightarrow Z$ +\end_inset + +. + It appears that we need the type +\begin_inset listings +inline true +status open \begin_layout Plain Layout +Z \end_layout -\begin_layout Plain Layout +\end_inset -def unfoldList[A, Z](f: Z => S[A, Z])(init: Z): List[A] = ??? -\end_layout + to contain a new depth-indexed tree. + The function +\begin_inset listings +inline true +status open \begin_layout Plain Layout +f \end_layout -\begin_layout Plain Layout +\end_inset -def powersOf2UpTo(n: Long): List[Long] = unfoldList(???)(???) -\end_layout + will be applied recursively to leaves and branches of a tree. + The depth-indexed index can be computed correctly only if the type +\begin_inset listings +inline true +status open \begin_layout Plain Layout +Z \end_layout -\begin_layout Plain Layout +\end_inset -scala> powersOf2UpTo(1000) -\end_layout + somehow keeps track of the current depth in the tree. + So, we cannot just set +\begin_inset listings +inline true +status open \begin_layout Plain Layout -res0: List[Long] = List(1, 2, 4, 8, 16, 32, 64, 128, 256, 512) +Z = T2[(A, Int)] \end_layout \end_inset - -\end_layout - -\begin_layout Subparagraph -Solution -\end_layout - -\begin_layout Standard -The recursion scheme for +. + We need to use a \begin_inset listings inline true status open \begin_layout Plain Layout -List +State \end_layout \end_inset - is -\begin_inset Formula $S^{A,R}\triangleq\bbnum 1+A\times R$ -\end_inset - -. - Let us specialize the code of + monad whose internal state (of type \begin_inset listings inline true status open \begin_layout Plain Layout -unfold +Int \end_layout \end_inset - from Eq. -\begin_inset space ~ -\end_inset +) represents the current depth and is updated automatically. + Let us define a suitable +\begin_inset listings +inline true +status open -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:unfold-via-recursion-scheme" -plural "false" -caps "false" -noprefix "false" +\begin_layout Plain Layout + +State +\end_layout \end_inset -) to the type + monad that we will denote by \begin_inset listings inline true status open \begin_layout Plain Layout -List[A] +St \end_layout \end_inset -: + for brevity: \begin_inset listings inline false status open \begin_layout Plain Layout -type S[A, R] = Option[(A, R)] +final case class St[A](run: Int => (A, Int)) { // A State monad with internal + state of type Int. \end_layout \begin_layout Plain Layout -def unfoldList[A, Z](f: Z => S[A, Z])(init: Z): List[A] = f(init) match - { + import io.chymyst.ch.implement // Derive these methods automatical +ly from types. \end_layout \begin_layout Plain Layout - case None => Nil + def flatMap[B](f: A => St[B]): St[B] = implement \end_layout \begin_layout Plain Layout - case Some((a, z)) => a :: unfoldList(f)(z) + def map[B](f: A => B): St[B] = implement \end_layout \begin_layout Plain Layout @@ -10912,283 +10925,342 @@ def unfoldList[A, Z](f: Z => S[A, Z])(init: Z): List[A] = f(init) match } \end_layout -\end_inset +\begin_layout Plain Layout + +def incrementAndGet: St[Int] = St(s => (s + 1, s + 1)) // Increment the + current state value. +\end_layout +\begin_layout Plain Layout +def get: St[Int] = St(s => (s, s)) // Fetch the current state + value. \end_layout -\begin_layout Standard -We now need to determine a suitable type -\begin_inset Formula $Z$ -\end_inset +\begin_layout Plain Layout + +def set(s: Int): St[Unit] = St(_ => ((), s)) // Set the state, ignore + previous state value. +\end_layout - and a suitable function -\begin_inset Formula $f:Z\rightarrow\bbnum 1+A\times Z$ \end_inset - so that the unfolding would produce the required sequence. - If we are in the middle of unfolding, we need to produce the remaining - portion of the list, say, -\begin_inset Formula $\left[128,256,512\right]$ +The folding result type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Z +\end_layout + \end_inset -, given only a current value of type -\begin_inset Formula $Z$ + will be defined as +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Z = St[T2[(A, Int)]] +\end_layout + \end_inset . - We can do that if the current value of type -\begin_inset Formula $Z$ -\end_inset + The wrapped value (of type +\begin_inset listings +inline true +status open - is -\begin_inset Formula $128$ -\end_inset +\begin_layout Plain Layout - (the smallest remaining power of -\begin_inset Formula $2$ -\end_inset +T2[(A, Int)] +\end_layout -). - So, let us choose -\begin_inset Formula $Z\triangleq$ \end_inset - +) inside the \begin_inset listings inline true status open \begin_layout Plain Layout -Long +State \end_layout \end_inset - to represent the smallest remaining power of -\begin_inset Formula $2$ -\end_inset + monad will be the final decorated tree. + To extract that tree, we will need to build a +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +State +\end_layout -. - The type -\begin_inset Formula $A$ \end_inset - will be also + monad program and run it with the initial depth value \begin_inset listings inline true status open \begin_layout Plain Layout -Long +0 \end_layout \end_inset . - + So, we expect the code of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +zipWithDepth \end_layout -\begin_layout Standard -How can we implement the function -\begin_inset Formula $f$ \end_inset -? It should return + to look like this: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -None +def zipWithDepth[A](tree: T2[A]): T2[(A, Int)] = \end_layout -\end_inset +\begin_layout Plain Layout - (denoted by -\begin_inset Formula $1+\bbnum 0$ -\end_inset + foldT2[A, St[T2[(A, Int)]]](f).run(0)._1 +\end_layout -) when the list is finished. - When we are in the middle of generating the list, the call -\begin_inset Formula $f(z)$ \end_inset - should return -\begin_inset Formula $\bbnum 0+a\times z^{\prime}$ -\end_inset - with some values -\begin_inset Formula $a$ -\end_inset +\end_layout - and -\begin_inset Formula $z^{\prime}$ -\end_inset +\begin_layout Standard +It remains to implement a suitable function +\begin_inset listings +inline true +status open -. - The value -\begin_inset Formula $a$ -\end_inset +\begin_layout Plain Layout - must be the new element of the list. - The value -\begin_inset Formula $z^{\prime}$ -\end_inset +f +\end_layout - will be passed to the next call of -\begin_inset Formula $f$ \end_inset -. - Since the next element must be twice the previous one, we must have -\begin_inset Formula $a=z$ -\end_inset + to which we will apply +\begin_inset listings +inline true +status open - and -\begin_inset Formula $z^{\prime}=z*2$ -\end_inset +\begin_layout Plain Layout + +foldT2 +\end_layout -. - So, the code of -\begin_inset Formula $f$ \end_inset - is: +. + The value of the internal state represents the current depth of the tree + element. + We need to increment that depth whenever we find a branch and then traverse + the two subtrees starting from the same depth value. + The final code is: \begin_inset listings inline false status open \begin_layout Plain Layout -def f(n: Long): Long => Option[(Long, Long)] = +def zipWithDepth[A](tree: T2[A]): T2[(A, Int)] = foldT2[A, St[T2[(A, Int)]]] + { \end_layout \begin_layout Plain Layout - { z => if (z >= n) None else Some((z, z * 2)) } + case Left(a) => for { s <- get } yield Leaf((a, s)) // Put the current + depth into the Leaf value. \end_layout -\end_inset +\begin_layout Plain Layout -Note that the code of -\begin_inset Formula $f$ -\end_inset + case Right((l, r)) => for { +\end_layout - is -\emph on -not -\emph default - recursive, and the value -\begin_inset Formula $n$ -\end_inset +\begin_layout Plain Layout + + s <- incrementAndGet // Read the current depth after incrementing + it. +\end_layout + +\begin_layout Plain Layout + + x <- l // Traverse the left branch starting from depth + `s`. +\end_layout + +\begin_layout Plain Layout + + _ <- set(s) // Set the same initial depth `s` for traversing + the right branch. + +\end_layout + +\begin_layout Plain Layout + + y <- r // Traverse the right branch. +\end_layout + +\begin_layout Plain Layout + + } yield Branch(x, y) +\end_layout + +\begin_layout Plain Layout + +}(tree).run(0)._1 +\end_layout - is captured inside the nameless function returned by -\begin_inset Formula $f(n)$ \end_inset -. - We can now complete the solution: +Here we need to use the \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def powersOf2UpTo(n: Long): List[Long] = unfoldList(f(n))(1) +State \end_layout \end_inset + monad's method +\begin_inset listings +inline true +status open -\end_layout +\begin_layout Plain Layout -\begin_layout Subsubsection -Example -\begin_inset CommandInset label -LatexCommand label -name "subsec:Example-unfold-tree" +set(s) +\end_layout \end_inset + with a value +\begin_inset listings +inline true +status open -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Example-unfold-tree" -plural "false" -caps "false" -noprefix "false" +\begin_layout Plain Layout -\end_inset +s +\end_layout +\end_inset + obtained from a previous monadic computation. \end_layout \begin_layout Standard -Implement +To test the code, apply \begin_inset listings inline true status open \begin_layout Plain Layout -unfoldT2 +zipWithDepth \end_layout \end_inset - for the data type + to a sample tree \begin_inset listings inline true status open \begin_layout Plain Layout -T2[A] +t2 \end_layout \end_inset - from Section + used earlier in Sections \begin_inset space ~ \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:Recursion-schemes.-folding" +reference "subsec:Decorating-a-tree-breadth-first-traversal" plural "false" caps "false" noprefix "false" \end_inset -. - Use it to create -\begin_inset Quotes eld -\end_inset + and +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Tasks-not-implementable-via-traverse" +plural "false" +caps "false" +noprefix "false" -full -\begin_inset Quotes erd \end_inset - binary trees of given depth, e.g., -\begin_inset Preview +: +\end_layout \begin_layout Standard +\begin_inset Wrap figure +lines 0 +placement l +overhang 0in +width "78col%" +status open -\size tiny -\begin_inset ERT +\begin_layout Plain Layout +\begin_inset VSpace -75baselineskip% +\end_inset + + +\begin_inset listings +inline false status open \begin_layout Plain Layout +scala> zipWithDepth(t2) +\end_layout -\backslash -Tree[ [ 0 1 ] [ 2 3 ] ] +\begin_layout Plain Layout + +res0: T2[(Int, Int)] = Branch(Leaf((8, 1)), Branch(Branch(Leaf((3, 3)), + Leaf((5, 3))), Leaf((4, 2)))) \end_layout \end_inset @@ -11196,13 +11268,24 @@ Tree[ [ 0 1 ] [ 2 3 ] ] \end_layout +\begin_layout Plain Layout +\begin_inset VSpace -50baselineskip% \end_inset - (depth -\begin_inset Formula $2$ + +\end_layout + \end_inset -) and + +\end_layout + +\begin_layout Standard +\noindent +\begin_inset space ~ +\end_inset + + \begin_inset Preview \begin_layout Standard @@ -11215,7 +11298,7 @@ status open \backslash -Tree[ [ [ 0 1 ] [ 2 3 ] ] [ [ 4 5 ] [ 6 7 ] ] ] +Tree[ (8,1) [ [ (3,3) (5,3) ] (4,2) ] ] \end_layout \end_inset @@ -11225,152 +11308,154 @@ Tree[ [ [ 0 1 ] [ 2 3 ] ] [ [ 4 5 ] [ 6 7 ] ] ] \end_inset - (depth -\begin_inset Formula $3$ -\end_inset - -). -\end_layout -\begin_layout Subparagraph -Solution \end_layout \begin_layout Standard -We adapt the general code in Eq. +\noindent \begin_inset space ~ \end_inset -( -\begin_inset CommandInset ref -LatexCommand ref -reference "eq:unfold-via-recursion-scheme" -plural "false" -caps "false" -noprefix "false" -\end_inset +\end_layout -) to obtain the code of +\begin_layout Standard +Note that the the actually used type of \begin_inset listings inline true status open \begin_layout Plain Layout -unfoldT2 +foldT2 \end_layout \end_inset -: + is somewhat similar to the type of \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -type S[A, R] = Either[A, (R, R)] -\end_layout - -\begin_layout Plain Layout - -def unfoldT2[A, Z](f: Z => S[A, Z])(init: Z): T2[A] = f(init) match { -\end_layout - -\begin_layout Plain Layout - - case Left(a) => Leaf(a) +traverse \end_layout -\begin_layout Plain Layout +\end_inset - case Right((z1, z2)) => Branch(unfoldT2(f)(z1), unfoldT2(f)(z2)) -\end_layout +: +\begin_inset Formula +\begin{align*} + & \text{foldT2}:\big(S^{A,F^{L^{B}}}\rightarrow F^{L^{B}}\big)\rightarrow L^{A}\rightarrow F^{L^{B}}\quad,\\ + & \text{trav}_{L}:(A\rightarrow F^{B})\rightarrow L^{A}\rightarrow F^{L^{B}}\quad. +\end{align*} -\begin_layout Plain Layout +\end_inset -} -\end_layout +(Here +\begin_inset Formula $L=\text{T2}$ +\end_inset + and +\begin_inset Formula $S$ \end_inset -We plan to implement the function + is its recursion scheme.) We apply \begin_inset listings inline true status open \begin_layout Plain Layout -fullBinaryTree +foldT2 \end_layout \end_inset - like this: -\begin_inset listings -inline false -status open - -\begin_layout Plain Layout - -def fullBinaryTree(n: Int): T2[Int] = { -\end_layout - -\begin_layout Plain Layout + to a function +\begin_inset Formula $f:S^{A,F^{L^{B}}}\rightarrow F^{L^{B}}$ +\end_inset - type Z = ??? -\end_layout + whose +\begin_inset Formula $F$ +\end_inset -\begin_layout Plain Layout +-effect is +\emph on +not +\emph default + equivalent to an applicative functor's effect: The function +\begin_inset Formula $f$ +\end_inset - val init: Z = ??? -\end_layout + assigns the +\begin_inset listings +inline true +status open \begin_layout Plain Layout - val f: Z => Either[Int, (Z, Z)] = ??? +State \end_layout -\begin_layout Plain Layout +\end_inset - unfoldT2[Int, Z](f)(init) -\end_layout + monad's internal state to a previously computed value, which applicative + effects cannot depend on a previous value. + A recursion scheme-based folding ( +\begin_inset Formula $\text{fold}_{S}$ +\end_inset -\begin_layout Plain Layout +) can use arbitrary functors +\begin_inset Formula $F$ +\end_inset -} -\end_layout +, including monads. + This is more powerful than the plain traversal, +\begin_inset Formula $\text{trav}_{L}(f^{:A\rightarrow F^{B}})$ +\end_inset +, that may only use applicative functors +\begin_inset Formula $F$ \end_inset -The next step is to choose a suitable type +. + Because of that extra power, we were able to implement \begin_inset listings inline true status open \begin_layout Plain Layout -Z +zipWithDepth \end_layout \end_inset - such that the unfolding procedure can generate trees of the required shape. - To figure out what -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout + via +\begin_inset Formula $\text{fold}_{S}$ +\end_inset -Z +. \end_layout +\begin_layout Subsection +Recursion schemes. + II. + Unfolding operations +\begin_inset CommandInset label +LatexCommand label +name "subsec:Recursion-schemes.-II.unfolding" + \end_inset - must be, we need to consider an intermediate step that generates a subtree - at some point in the middle of + +\end_layout + +\begin_layout Standard +A folding operation converts a collection to a single value. + The opposite operation is \begin_inset Quotes eld \end_inset @@ -11378,296 +11463,293 @@ unfolding \begin_inset Quotes erd \end_inset -. - For the tree of depth -\begin_inset Formula $3$ +: converting a single value into a collection. + By reversing the direction of certain function arrows in the type signature + of +\begin_inset Formula $\text{fold}_{S}$ \end_inset - as shown above, an example of a subtree in the middle is -\begin_inset Preview +, we can define a general +\begin_inset Quotes eld +\end_inset -\begin_layout Standard +unfolding +\begin_inset Quotes erd +\end_inset -\size tiny -\begin_inset ERT -status open + method that uses an arbitrary recursion scheme +\begin_inset Formula $S$ +\end_inset -\begin_layout Plain Layout + and an arbitrary function of type +\begin_inset Formula $Z\rightarrow S^{A,Z}$ +\end_inset +: +\begin_inset Formula +\begin{equation} +\text{unfold}_{S}:(Z\rightarrow S^{A,Z})\rightarrow Z\rightarrow L^{A}\quad,\quad\text{unfold}_{S}(f)\triangleq f\bef\overline{\text{unfold}_{S}(f)}^{\uparrow S^{A,\bullet}}\quad.\label{eq:unfold-via-recursion-scheme} +\end{equation} -\backslash -Tree[ 4 5 ] -\end_layout +\end_inset +Section +\begin_inset space ~ \end_inset -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "sec:ch2Converting-a-single" +plural "false" +caps "false" +noprefix "false" \end_inset -. - This subtree must be computed as + showed an unfolding operation for sequences: starting from an initial value, + a function is applied repeatedly to compute further elements of the sequence. + The \begin_inset listings inline true status open \begin_layout Plain Layout -unfoldT2(f)(z) +unfold \end_layout \end_inset - with some -\begin_inset listings -inline true -status open + operation generalizes that computation to an arbitrary recursive type construct +or +\begin_inset Formula $L$ +\end_inset -\begin_layout Plain Layout + whose recursion scheme +\begin_inset Formula $S$ +\end_inset -z + is given. + \end_layout -\end_inset - - of type +\begin_layout Standard +To get more intuition, we look at some examples using \begin_inset listings inline true status open \begin_layout Plain Layout -Z +unfold \end_layout \end_inset -. - The information needed for this computation is the initial value ( -\begin_inset Formula $4$ -\end_inset + with lists and binary trees. +\end_layout -) and the total number ( -\begin_inset Formula $2$ -\end_inset +\begin_layout Subsubsection +Example +\begin_inset CommandInset label +LatexCommand label +name "subsec:Example-unfold-list" -) of the subtree's leaves. - So, the type -\begin_inset listings -inline true -status open +\end_inset -\begin_layout Plain Layout -Z -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Example-unfold-list" +plural "false" +caps "false" +noprefix "false" \end_inset - needs to contain two integers: the value of the first leaf and the size - of the remaining subtree (which will always be a power of -\begin_inset Formula $2$ -\end_inset -). -\begin_inset listings -lstparams "mathescape=true" -inline false +\begin_inset Index idx status open \begin_layout Plain Layout - -type Z = (Int, Int) // (k, m) where k is the first leaf$ -\backslash -color{dkgreen} -\backslash -texttt{'}$s value and m is the subtree size. +examples (with code) \end_layout -\begin_layout Plain Layout +\end_inset -val init: Z = (0, 1 << n) // The size of the entire tree is 2 to the power - n. -\end_layout -\end_inset +\end_layout -The initial value for the entire tree shown above will be +\begin_layout Standard +Use \begin_inset listings inline true status open \begin_layout Plain Layout -(0, 8) +unfold \end_layout \end_inset -. -\end_layout - -\begin_layout Standard -Next, we figure out the function + to create a \begin_inset listings inline true status open \begin_layout Plain Layout -f +List \end_layout \end_inset -. - When + of consecutive powers of +\begin_inset Formula $2$ +\end_inset + + up to a given value +\begin_inset Formula $n$ +\end_inset + +: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -f +type S[A, R] = ??? \end_layout -\end_inset +\begin_layout Plain Layout - is applied to a value -\begin_inset listings -inline true -status open +type Z = ??? +\end_layout \begin_layout Plain Layout -(k, m) \end_layout -\end_inset +\begin_layout Plain Layout - of type -\begin_inset listings -inline true -status open +def unfoldList[A, Z](f: Z => S[A, Z])(init: Z): List[A] = ??? +\end_layout \begin_layout Plain Layout -Z \end_layout -\end_inset +\begin_layout Plain Layout -, it should generate the corresponding subtree. - If the subtree size -\begin_inset listings -inline true -status open +def powersOf2UpTo(n: Long): List[Long] = unfoldList(???)(???) +\end_layout \begin_layout Plain Layout -m \end_layout -\end_inset - - is -\begin_inset Formula $1$ -\end_inset +\begin_layout Plain Layout -, the return value is -\begin_inset listings -inline true -status open +scala> powersOf2UpTo(1000) +\end_layout \begin_layout Plain Layout -Left(k) +res0: List[Long] = List(1, 2, 4, 8, 16, 32, 64, 128, 256, 512) \end_layout \end_inset -. - Otherwise, the return value should give two new values of type -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -Z \end_layout -\end_inset +\begin_layout Subparagraph +Solution +\end_layout - corresponding to the two subtrees one level deeper. - Those two subtrees have sizes +\begin_layout Standard +The recursion scheme for \begin_inset listings inline true status open \begin_layout Plain Layout -m / 2 +List \end_layout \end_inset - and initial values + is +\begin_inset Formula $S^{A,R}\triangleq\bbnum 1+A\times R$ +\end_inset + +. + Let us specialize the code of \begin_inset listings inline true status open \begin_layout Plain Layout -k +unfold \end_layout \end_inset - and -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout + from Eq. +\begin_inset space ~ +\end_inset -m / 2 + k -\end_layout +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:unfold-via-recursion-scheme" +plural "false" +caps "false" +noprefix "false" \end_inset -. - So, the code of +) to the type \begin_inset listings inline true status open \begin_layout Plain Layout -f +List[A] \end_layout \end_inset - is: +: \begin_inset listings inline false status open \begin_layout Plain Layout -val f: Z => Either[Int, (Z, Z)] = { +type S[A, R] = Option[(A, R)] \end_layout \begin_layout Plain Layout - case (k, m) if m == 1 => Left(k) +def unfoldList[A, Z](f: Z => S[A, Z])(init: Z): List[A] = f(init) match + { \end_layout \begin_layout Plain Layout - case (k, m) => Right(((k, m / 2), (m / 2 + k, m / 2))) + case None => Nil +\end_layout + +\begin_layout Plain Layout + + case Some((a, z)) => a :: unfoldList(f)(z) \end_layout \begin_layout Plain Layout @@ -11681,180 +11763,264 @@ val f: Z => Either[Int, (Z, Z)] = { \end_layout \begin_layout Standard -This completes the implementation of -\begin_inset listings -inline true -status open +We now need to determine a suitable type +\begin_inset Formula $Z$ +\end_inset -\begin_layout Plain Layout + and a suitable function +\begin_inset Formula $f:Z\rightarrow\bbnum 1+A\times Z$ +\end_inset -fullBinaryTree -\end_layout + so that the unfolding would produce the required sequence. + If we are in the middle of unfolding, we need to produce the remaining + portion of the list, say, +\begin_inset Formula $\left[128,256,512\right]$ +\end_inset +, given only a current value of type +\begin_inset Formula $Z$ \end_inset . - To test the resulting code, compute a full tree of depth -\begin_inset Formula $2$ + We can do that if the current value of type +\begin_inset Formula $Z$ \end_inset -, which we expect to be -\begin_inset space ~ + is +\begin_inset Formula $128$ \end_inset + (the smallest remaining power of +\begin_inset Formula $2$ +\end_inset -\begin_inset Preview - -\begin_layout Standard +). + So, let us choose +\begin_inset Formula $Z\triangleq$ +\end_inset -\size tiny -\begin_inset ERT + +\begin_inset listings +inline true status open \begin_layout Plain Layout - -\backslash -Tree[ [ 0 1 ] [ 2 3 ] ] +Long \end_layout \end_inset + to represent the smallest remaining power of +\begin_inset Formula $2$ +\end_inset -\end_layout - +. + The type +\begin_inset Formula $A$ \end_inset -: + will be also \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -scala> fullBinaryTree(2) +Long \end_layout -\begin_layout Plain Layout +\end_inset -res0: T2[Int] = Branch(Branch(Leaf(0), Leaf(1)), Branch(Leaf(2), Leaf(3))) +. + \end_layout +\begin_layout Standard +How can we implement the function +\begin_inset Formula $f$ \end_inset +? It should return +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +None \end_layout -\begin_layout Subsubsection -Example -\begin_inset CommandInset label -LatexCommand label -name "subsec:Example-unfold-tree-evenodd" +\end_inset + (denoted by +\begin_inset Formula $1+\bbnum 0$ \end_inset +) when the list is finished. + When we are in the middle of generating the list, the call +\begin_inset Formula $f(z)$ +\end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Example-unfold-tree-evenodd" -plural "false" -caps "false" -noprefix "false" + should return +\begin_inset Formula $\bbnum 0+a\times z^{\prime}$ +\end_inset + with some values +\begin_inset Formula $a$ \end_inset + and +\begin_inset Formula $z^{\prime}$ +\end_inset -\end_layout +. + The value +\begin_inset Formula $a$ +\end_inset -\begin_layout Standard -Use + must be the new element of the list. + The value +\begin_inset Formula $z^{\prime}$ +\end_inset + + will be passed to the next call of +\begin_inset Formula $f$ +\end_inset + +. + Since the next element must be twice the previous one, we must have +\begin_inset Formula $a=z$ +\end_inset + + and +\begin_inset Formula $z^{\prime}=z*2$ +\end_inset + +. + So, the code of +\begin_inset Formula $f$ +\end_inset + + is: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -unfoldT2 +def f(n: Long): Long => Option[(Long, Long)] = \end_layout -\end_inset +\begin_layout Plain Layout + + { z => if (z >= n) None else Some((z, z * 2)) } +\end_layout - from Example -\begin_inset space ~ \end_inset +Note that the code of +\begin_inset Formula $f$ +\end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Example-unfold-tree" -plural "false" -caps "false" -noprefix "false" + is +\emph on +not +\emph default + recursive, and the value +\begin_inset Formula $n$ +\end_inset + is captured inside the nameless function returned by +\begin_inset Formula $f(n)$ \end_inset - to implement the function +. + We can now complete the solution: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -evenOdd(n) +def powersOf2UpTo(n: Long): List[Long] = unfoldList(f(n))(1) \end_layout \end_inset - that generates binary trees of type -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -T2[Int] \end_layout +\begin_layout Subsubsection +Example +\begin_inset CommandInset label +LatexCommand label +name "subsec:Example-unfold-tree" + \end_inset - where the leaves have descending numbers from + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Example-unfold-tree" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + +\end_layout + +\begin_layout Standard +Implement \begin_inset listings inline true status open \begin_layout Plain Layout -n +unfoldT2 \end_layout \end_inset - to + for the data type \begin_inset listings inline true status open \begin_layout Plain Layout -0 +T2[A] \end_layout \end_inset -, but all odd numbers are on the left and all even numbers on the right. - For example, -\begin_inset listings -inline true -status open + from Section +\begin_inset space ~ +\end_inset -\begin_layout Plain Layout -evenOdd(3) -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Recursion-schemes.-folding" +plural "false" +caps "false" +noprefix "false" \end_inset - should generate the tree +. + Use it to create +\begin_inset Quotes eld +\end_inset + +full +\begin_inset Quotes erd +\end_inset + + binary trees of given depth, e.g., \begin_inset Preview \begin_layout Standard @@ -11867,7 +12033,7 @@ status open \backslash -Tree[ 3 [ [ 1 0 ] 2 ] ] +Tree[ [ 0 1 ] [ 2 3 ] ] \end_layout \end_inset @@ -11877,19 +12043,11 @@ Tree[ 3 [ [ 1 0 ] 2 ] ] \end_inset - and -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -evenOdd(4) -\end_layout - + (depth +\begin_inset Formula $2$ \end_inset - should give the tree +) and \begin_inset Preview \begin_layout Standard @@ -11902,7 +12060,7 @@ status open \backslash -Tree[ [ 3 [ [ 1 0 ] 2 ] ] 4 ] +Tree[ [ [ 0 1 ] [ 2 3 ] ] [ [ 4 5 ] [ 6 7 ] ] ] \end_layout \end_inset @@ -11912,7 +12070,11 @@ Tree[ [ 3 [ [ 1 0 ] 2 ] ] 4 ] \end_inset - . + (depth +\begin_inset Formula $3$ +\end_inset + +). \end_layout \begin_layout Subparagraph @@ -11920,46 +12082,55 @@ Solution \end_layout \begin_layout Standard -We plan to implement +We adapt the general code in Eq. +\begin_inset space ~ +\end_inset + +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:unfold-via-recursion-scheme" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +) to obtain the code of \begin_inset listings inline true status open \begin_layout Plain Layout -evenOdd +unfoldT2 \end_layout \end_inset - like this: +: \begin_inset listings inline false status open \begin_layout Plain Layout -def evenOdd(n: Int): T2[Int] = { -\end_layout - -\begin_layout Plain Layout - - type Z = ??? +type S[A, R] = Either[A, (R, R)] \end_layout \begin_layout Plain Layout - val init: Z = ??? +def unfoldT2[A, Z](f: Z => S[A, Z])(init: Z): T2[A] = f(init) match { \end_layout \begin_layout Plain Layout - val f: Z => Either[Int, (Z, Z)] = ??? + case Left(a) => Leaf(a) \end_layout \begin_layout Plain Layout - unfoldT2[Int, Z](f)(init) + case Right((z1, z2)) => Branch(unfoldT2(f)(z1), unfoldT2(f)(z2)) \end_layout \begin_layout Plain Layout @@ -11969,109 +12140,131 @@ def evenOdd(n: Int): T2[Int] = { \end_inset -The examples with +We plan to implement the function \begin_inset listings inline true status open \begin_layout Plain Layout -evenOdd(3) +fullBinaryTree \end_layout \end_inset - and + like this: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -evenOdd(4) +def fullBinaryTree(n: Int): T2[Int] = { \end_layout -\end_inset +\begin_layout Plain Layout - suggest that -\begin_inset listings -inline true -status open + type Z = ??? +\end_layout \begin_layout Plain Layout -evenOdd(n) + val init: Z = ??? \end_layout -\end_inset +\begin_layout Plain Layout - is a tree containing a leaf with value -\begin_inset listings -inline true -status open + val f: Z => Either[Int, (Z, Z)] = ??? +\end_layout \begin_layout Plain Layout -n + unfoldT2[Int, Z](f)(init) +\end_layout + +\begin_layout Plain Layout + +} \end_layout \end_inset - and a subtree +The next step is to choose a suitable type \begin_inset listings inline true status open \begin_layout Plain Layout -evenOdd(n - 1) +Z \end_layout \end_inset -. - Can we use the integer + such that the unfolding procedure can generate trees of the required shape. + To figure out what \begin_inset listings inline true status open \begin_layout Plain Layout -n +Z \end_layout \end_inset - as the initial value for unfolding? -\end_layout + must be, we need to consider an intermediate step that generates a subtree + at some point in the middle of +\begin_inset Quotes eld +\end_inset + +unfolding +\begin_inset Quotes erd +\end_inset + +. + For the tree of depth +\begin_inset Formula $3$ +\end_inset + + as shown above, an example of a subtree in the middle is +\begin_inset Preview \begin_layout Standard -To figure this out, consider an intermediate stage of the unfolding process - where -\begin_inset listings -inline true + +\size tiny +\begin_inset ERT status open \begin_layout Plain Layout -unfoldT2 + +\backslash +Tree[ 4 5 ] \end_layout \end_inset - will apply the function + +\end_layout + +\end_inset + +. + This subtree must be computed as \begin_inset listings inline true status open \begin_layout Plain Layout -f +unfoldT2(f)(z) \end_layout \end_inset - to some value + with some \begin_inset listings inline true status open @@ -12096,163 +12289,183 @@ Z \end_inset . - If + The information needed for this computation is the initial value ( +\begin_inset Formula $4$ +\end_inset + +) and the total number ( +\begin_inset Formula $2$ +\end_inset + +) of the subtree's leaves. + So, the type \begin_inset listings inline true status open \begin_layout Plain Layout -f(z) == Left(k) +Z \end_layout \end_inset -, we will get a + needs to contain two integers: the value of the first leaf and the size + of the remaining subtree (which will always be a power of +\begin_inset Formula $2$ +\end_inset + +). \begin_inset listings -inline true +lstparams "mathescape=true" +inline false status open \begin_layout Plain Layout -Leaf +type Z = (Int, Int) // (k, m) where k is the first leaf$ +\backslash +color{dkgreen} +\backslash +texttt{'}$s value and m is the subtree size. \end_layout -\end_inset - - with value -\begin_inset listings -inline true -status open - \begin_layout Plain Layout -k +val init: Z = (0, 1 << n) // The size of the entire tree is 2 to the power + n. \end_layout \end_inset -. - The other possibility is +The initial value for the entire tree shown above will be \begin_inset listings inline true status open \begin_layout Plain Layout -f(z) == Right((z1, z2)) +(0, 8) \end_layout \end_inset -, where +. +\end_layout + +\begin_layout Standard +Next, we figure out the function \begin_inset listings inline true status open \begin_layout Plain Layout -z1 +f \end_layout \end_inset - and +. + When \begin_inset listings inline true status open \begin_layout Plain Layout -z2 +f \end_layout \end_inset - are some values of type + is applied to a value \begin_inset listings inline true status open \begin_layout Plain Layout -Z +(k, m) \end_layout \end_inset -. - This will generate a + of type \begin_inset listings inline true status open \begin_layout Plain Layout -Branch +Z \end_layout \end_inset - with two subtrees. - The function +, it should generate the corresponding subtree. + If the subtree size \begin_inset listings inline true status open \begin_layout Plain Layout -unfoldT2 +m \end_layout \end_inset - will then apply + is +\begin_inset Formula $1$ +\end_inset + +, the return value is \begin_inset listings inline true status open \begin_layout Plain Layout -f +Left(k) \end_layout \end_inset - to +. + Otherwise, the return value should give two new values of type \begin_inset listings inline true status open \begin_layout Plain Layout -z1 +Z \end_layout \end_inset - and + corresponding to the two subtrees one level deeper. + Those two subtrees have sizes \begin_inset listings inline true status open \begin_layout Plain Layout -z2 +m / 2 \end_layout \end_inset - in order to create the left and the right subtrees. - The difference between those subtrees must come entirely from the difference - between the values + and initial values \begin_inset listings inline true status open \begin_layout Plain Layout -z1 +k \end_layout \end_inset @@ -12264,363 +12477,334 @@ status open \begin_layout Plain Layout -z2 +m / 2 + k \end_layout \end_inset . - -\end_layout - -\begin_layout Standard -In our case, we need to make a + So, the code of \begin_inset listings inline true status open \begin_layout Plain Layout -Leaf +f \end_layout \end_inset - either at the left or at the right depending on whether the initial leaf - value is odd or even. - The function + is: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -f +val f: Z => Either[Int, (Z, Z)] = { \end_layout -\end_inset - - must know whether -\begin_inset listings -inline true -status open - \begin_layout Plain Layout -f(z) + case (k, m) if m == 1 => Left(k) \end_layout -\end_inset +\begin_layout Plain Layout - should return a subtree or a -\begin_inset listings -inline true -status open + case (k, m) => Right(((k, m / 2), (m / 2 + k, m / 2))) +\end_layout \begin_layout Plain Layout -Leaf +} \end_layout \end_inset -. - This information can only come from the value -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -z \end_layout -\end_inset - -. - So, the type +\begin_layout Standard +This completes the implementation of \begin_inset listings inline true status open \begin_layout Plain Layout -Z +fullBinaryTree \end_layout \end_inset - must contain a -\begin_inset listings -inline true -status open +. + To test the resulting code, compute a full tree of depth +\begin_inset Formula $2$ +\end_inset -\begin_layout Plain Layout +, which we expect to be +\begin_inset space ~ +\end_inset -Boolean -\end_layout -\end_inset +\begin_inset Preview - flag saying whether we need a -\begin_inset listings -inline true +\begin_layout Standard + +\size tiny +\begin_inset ERT status open \begin_layout Plain Layout -Leaf + +\backslash +Tree[ [ 0 1 ] [ 2 3 ] ] \end_layout \end_inset - at the current place. - For clarity, let us define the type -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -Z \end_layout \end_inset - as a case class: +: \begin_inset listings inline false status open \begin_layout Plain Layout -final case class Z(startAt: Int, makeLeaf: Boolean) +scala> fullBinaryTree(2) \end_layout \begin_layout Plain Layout -val init = Z(startAt = n, makeLeaf = false) +res0: T2[Int] = Branch(Branch(Leaf(0), Leaf(1)), Branch(Leaf(2), Leaf(3))) \end_layout \end_inset -When -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -makeLeaf \end_layout -\end_inset - - is -\begin_inset listings -inline true -status open +\begin_layout Subsubsection +Example +\begin_inset CommandInset label +LatexCommand label +name "subsec:Example-unfold-tree-evenodd" -\begin_layout Plain Layout +\end_inset -true -\end_layout + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Example-unfold-tree-evenodd" +plural "false" +caps "false" +noprefix "false" \end_inset -, we must create a + +\end_layout + +\begin_layout Standard +Use \begin_inset listings inline true status open \begin_layout Plain Layout -Leaf +unfoldT2 \end_layout \end_inset -, so -\begin_inset listings -inline true -status open + from Example +\begin_inset space ~ +\end_inset -\begin_layout Plain Layout -f -\end_layout +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Example-unfold-tree" +plural "false" +caps "false" +noprefix "false" \end_inset - should return a + to implement the function \begin_inset listings inline true status open \begin_layout Plain Layout -Left() +evenOdd(n) \end_layout \end_inset -. - Otherwise + that generates binary trees of type \begin_inset listings inline true status open \begin_layout Plain Layout -f +T2[Int] \end_layout \end_inset - should return a + where the leaves have descending numbers from \begin_inset listings inline true status open \begin_layout Plain Layout -Right((z1, z2)) +n \end_layout \end_inset -, where + to \begin_inset listings inline true status open \begin_layout Plain Layout -z1 +0 \end_layout \end_inset - and +, but all odd numbers are on the left and all even numbers on the right. + For example, \begin_inset listings inline true status open \begin_layout Plain Layout -z2 +evenOdd(3) \end_layout \end_inset - should set -\begin_inset listings -inline true + should generate the tree +\begin_inset Preview + +\begin_layout Standard + +\size tiny +\begin_inset ERT status open \begin_layout Plain Layout -makeLeaf + +\backslash +Tree[ 3 [ [ 1 0 ] 2 ] ] \end_layout \end_inset - depending on -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -startAt \end_layout \end_inset - being odd or even. - Looking at the examples, we find that + and \begin_inset listings inline true status open \begin_layout Plain Layout -f +evenOdd(4) \end_layout \end_inset - must also return a -\begin_inset listings -inline true + should give the tree +\begin_inset Preview + +\begin_layout Standard + +\size tiny +\begin_inset ERT status open \begin_layout Plain Layout -Left() + +\backslash +Tree[ [ 3 [ [ 1 0 ] 2 ] ] 4 ] \end_layout \end_inset - when -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -startAt == 0 \end_layout \end_inset -. - So, the code of + . +\end_layout + +\begin_layout Subparagraph +Solution +\end_layout + +\begin_layout Standard +We plan to implement \begin_inset listings inline true status open \begin_layout Plain Layout -f +evenOdd \end_layout \end_inset - is: + like this: \begin_inset listings inline false status open \begin_layout Plain Layout -val f: Z => Either[Int, (Z, Z)] = { -\end_layout - -\begin_layout Plain Layout - - case Z(n, false) if n > 0 && n % 2 == 0 => +def evenOdd(n: Int): T2[Int] = { \end_layout \begin_layout Plain Layout - Right((Z(n - 1, false), Z(n, true))) + type Z = ??? \end_layout \begin_layout Plain Layout - case Z(n, false) if n > 0 && n % 2 == 1 => + val init: Z = ??? \end_layout \begin_layout Plain Layout - Right((Z(n, true), Z(n - 1, false))) + val f: Z => Either[Int, (Z, Z)] = ??? \end_layout \begin_layout Plain Layout - case Z(n, _) => Left(n) // Make a leaf when - n == 0 or makeLeaf == true. + unfoldT2[Int, Z](f)(init) \end_layout \begin_layout Plain Layout @@ -12630,626 +12814,681 @@ val f: Z => Either[Int, (Z, Z)] = { \end_inset -The implementation of +The examples with \begin_inset listings inline true status open \begin_layout Plain Layout -evenOdd +evenOdd(3) \end_layout \end_inset - is now complete. - To test: -\end_layout - -\begin_layout Standard -\begin_inset Wrap figure -lines 0 -placement l -overhang 0in -width "84col%" + and +\begin_inset listings +inline true status open \begin_layout Plain Layout -\begin_inset VSpace -95baselineskip% -\end_inset +evenOdd(4) +\end_layout + +\end_inset + suggest that \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -scala> evenOdd(3) +evenOdd(n) \end_layout +\end_inset + + is a tree containing a leaf with value +\begin_inset listings +inline true +status open + \begin_layout Plain Layout -res0: T2[Int] = Branch(Leaf(3), Branch(Branch(Leaf(1), Leaf(0)), Leaf(2))) +n \end_layout \end_inset - -\end_layout + and a subtree +\begin_inset listings +inline true +status open \begin_layout Plain Layout -\begin_inset VSpace 0baselineskip% -\end_inset - +evenOdd(n - 1) \end_layout \end_inset +. + Can we use the integer +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +n \end_layout -\begin_layout Standard -\noindent -\begin_inset VSpace 30baselineskip% \end_inset - -\begin_inset Preview + as the initial value for unfolding? +\end_layout \begin_layout Standard - -\size tiny -\begin_inset ERT +To figure this out, consider an intermediate stage of the unfolding process + where +\begin_inset listings +inline true status open \begin_layout Plain Layout - -\backslash -Tree[ 3 [ [ 1 0 ] 2 ] ] +unfoldT2 \end_layout \end_inset + will apply the function +\begin_inset listings +inline true +status open +\begin_layout Plain Layout + +f \end_layout \end_inset + to some value +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +z +\end_layout -\begin_inset Newline newline \end_inset + of type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +Z \end_layout -\begin_layout Standard -\noindent -\begin_inset Formula $\square$ \end_inset +. + If +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +f(z) == Left(k) \end_layout -\begin_layout Standard -The kind of reasoning shown in Examples -\begin_inset space ~ \end_inset +, we will get a +\begin_inset listings +inline true +status open -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Example-unfold-list" -plural "false" -caps "false" -noprefix "false" - -\end_inset +\begin_layout Plain Layout -– -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Example-unfold-tree-evenodd" -plural "false" -caps "false" -noprefix "false" +Leaf +\end_layout \end_inset - is known as reasoning by -\series bold -co-induction -\series default - -\begin_inset Index idx + with value +\begin_inset listings +inline true status open \begin_layout Plain Layout -co-induction + +k \end_layout \end_inset . - It is related to mathematical induction but is significantly different - from the reasoning required to write the code for a folding operation (which - is directly modeled on induction). - Note that + The other possibility is \begin_inset listings inline true status open \begin_layout Plain Layout -unfold(f)(z) +f(z) == Right((z1, z2)) \end_layout \end_inset - will call itself whenever the value -\begin_inset Formula $f(z)$ -\end_inset - - of type -\begin_inset Formula $S^{A,Z}$ -\end_inset +, where +\begin_inset listings +inline true +status open - contains additional values of type -\begin_inset Formula $Z$ -\end_inset +\begin_layout Plain Layout -. - The programmer must carefully choose a suitable type -\begin_inset Formula $Z$ -\end_inset +z1 +\end_layout - and a suitable function -\begin_inset Formula $f$ \end_inset - such that + and \begin_inset listings inline true status open \begin_layout Plain Layout -unfold(f)(z) +z2 \end_layout \end_inset - stops the recursion at the required places. - For instance, if -\begin_inset Formula $S^{A,Z}\triangleq A+Z\times Z$ -\end_inset - -, the function -\begin_inset Formula $f$ -\end_inset + are some values of type +\begin_inset listings +inline true +status open - must sometimes return a value of type -\begin_inset Formula $A+\bbnum 0$ -\end_inset +\begin_layout Plain Layout - to stop the unfolding. - One could say that the base cases in co-induction are not at the beginning - of the computation but -\begin_inset Quotes eld -\end_inset +Z +\end_layout -in the future -\begin_inset Quotes erd \end_inset . - -\end_layout - -\begin_layout Standard -Is the recursion guaranteed to stop while evaluating + This will generate a \begin_inset listings inline true status open \begin_layout Plain Layout -unfold +Branch \end_layout \end_inset -? The type signature -\begin_inset Formula $f:Z\rightarrow S^{A,Z}$ -\end_inset - - itself does not guarantee that -\begin_inset Formula $f(z)$ -\end_inset - - returns values that will stop the unfolding at the right places. - If -\begin_inset Formula $S^{A,Z}\triangleq A+Z\times Z$ -\end_inset + with two subtrees. + The function +\begin_inset listings +inline true +status open - and -\begin_inset Formula $f(z)$ -\end_inset +\begin_layout Plain Layout - always returns values of type -\begin_inset Formula $\bbnum 0+Z\times Z$ -\end_inset +unfoldT2 +\end_layout - (for example, -\begin_inset Formula $f(z)\triangleq\bbnum 0+z\times z$ \end_inset -), the unfolding operation + will then apply \begin_inset listings inline true status open \begin_layout Plain Layout -unfold(f) +f \end_layout \end_inset - will enter an infinite loop trying to construct a tree of infinite size. - This will, of course, fail since data structures in a computer cannot have - infinite size. -\end_layout + to +\begin_inset listings +inline true +status open -\begin_layout Standard -Unfolding will always terminate if we use a data type that -\emph on -delays -\emph default - the evaluation of its recursively defined parts. - Those parts will be computed only on demand. - To obtain further data, the code needs to call certain functions. - Data types of this kind are sometimes called -\begin_inset Quotes eld -\end_inset +\begin_layout Plain Layout + +z1 +\end_layout -infinite -\begin_inset Quotes erd \end_inset -, -\begin_inset Index idx + and +\begin_inset listings +inline true status open \begin_layout Plain Layout -infinite data types -\end_layout - -\end_inset - which is misleading since only a finite amount of data is ever stored in - memory. +z2 \end_layout -\begin_layout Standard -As an example, consider the recursion scheme -\begin_inset Formula $S^{A,R}\triangleq A+(\bbnum 1\rightarrow R\times R)$ \end_inset -. - The corresponding data type -\begin_inset Formula $L^{A}\triangleq S^{A,L^{A}}$ -\end_inset + in order to create the left and the right subtrees. + The difference between those subtrees must come entirely from the difference + between the values +\begin_inset listings +inline true +status open - is a binary tree whose branches are evaluated on demand (but leaves are - evaluated eagerly). - This data structure supports unfolding with -\emph on -any -\emph default - function -\begin_inset Formula $f:Z\rightarrow S^{A,Z}$ -\end_inset +\begin_layout Plain Layout + +z1 +\end_layout - because the recursive evaluation of -\begin_inset Formula $f$ \end_inset - at the branches is always delayed. - For instance, + and \begin_inset listings inline true status open \begin_layout Plain Layout -unfold +z2 \end_layout \end_inset - can generate a value of type -\begin_inset Formula $L^{A}$ -\end_inset - - whose tree structure has the form -\begin_inset Preview +. + +\end_layout \begin_layout Standard - -\size tiny -\begin_inset ERT +In our case, we need to make a +\begin_inset listings +inline true status open \begin_layout Plain Layout - -\backslash -Tree[ 1 [ 2 [3 ... - ] ] ] +Leaf \end_layout \end_inset + either at the left or at the right depending on whether the initial leaf + value is odd or even. + The function +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +f \end_layout \end_inset - with unbounded size: -\begin_inset VSpace 60baselineskip% -\end_inset - - + must know whether \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -type S[A, R] = Either[A, Unit => (R, R)] +f(z) \end_layout -\begin_layout Plain Layout +\end_inset -\end_layout + should return a subtree or a +\begin_inset listings +inline true +status open \begin_layout Plain Layout -sealed trait UT[A] // A tree with on-call evaluation of branches and - eager evaluation of leaves. +Leaf \end_layout -\begin_layout Plain Layout - -case class ULeaf[A](a: A) extends UT[A] -\end_layout +\end_inset -\begin_layout Plain Layout - -case class UBranch[A](run: Unit => (UT[A], UT[A])) extends UT[A] // Call - run(()) to get the branches. -\end_layout +. + This information can only come from the value +\begin_inset listings +inline true +status open \begin_layout Plain Layout +z \end_layout -\begin_layout Plain Layout +\end_inset -def unfoldUT[A, Z](f: Z => S[A, Z])(init: Z): UT[A] = f(init) match { -\end_layout +. + So, the type +\begin_inset listings +inline true +status open \begin_layout Plain Layout - case Left(a) => ULeaf(a) +Z \end_layout -\begin_layout Plain Layout +\end_inset - case Right(func) => UBranch { _ => // It is important to delay the - evaluation of func(()). -\end_layout + must contain a +\begin_inset listings +inline true +status open \begin_layout Plain Layout - val (z1, z2) = func(()) // Force the evaluation of branches - at this level. +Boolean \end_layout -\begin_layout Plain Layout +\end_inset - (unfoldUT(f)(z1), unfoldUT(f)(z2)) // `unfoldUT` will delay the - evaluation of further branches. -\end_layout + flag saying whether we need a +\begin_inset listings +inline true +status open \begin_layout Plain Layout - } +Leaf \end_layout -\begin_layout Plain Layout +\end_inset -} -\end_layout + at the current place. + For clarity, let us define the type +\begin_inset listings +inline true +status open \begin_layout Plain Layout +Z \end_layout -\begin_layout Plain Layout +\end_inset -val tree1toInf = unfoldUT[Int, (Int, Boolean)] { case (z, makeLeaf) => -\end_layout + as a case class: +\begin_inset listings +inline false +status open \begin_layout Plain Layout - if (makeLeaf) Left(z) else Right(_ => ((z + 1, true), (z + 1, false))) +final case class Z(startAt: Int, makeLeaf: Boolean) \end_layout \begin_layout Plain Layout -}((0, false)) +val init = Z(startAt = n, makeLeaf = false) \end_layout \end_inset -The value +When \begin_inset listings inline true status open \begin_layout Plain Layout -tree1toInf +makeLeaf \end_layout \end_inset - is finite but can compute a tree of unbounded depth. - To visualize + is \begin_inset listings inline true status open \begin_layout Plain Layout -tree1toInf +true \end_layout \end_inset -, we write a function that converts +, we must create a \begin_inset listings inline true status open \begin_layout Plain Layout -UT[A] +Leaf \end_layout \end_inset - to +, so \begin_inset listings inline true status open \begin_layout Plain Layout -T2[A] +f \end_layout \end_inset - by stopping at a given maximum depth. - The unevaluated parts of the tree will be marked with a value called + should return a \begin_inset listings inline true status open \begin_layout Plain Layout -default +Left() \end_layout \end_inset -: +. + Otherwise \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -def toT2[A](maxDepth: Int, default: A): UT[A] => T2[A] = { +f \end_layout +\end_inset + + should return a +\begin_inset listings +inline true +status open + \begin_layout Plain Layout - case ULeaf(a) => Leaf(a)e +Right((z1, z2)) \end_layout +\end_inset + +, where +\begin_inset listings +inline true +status open + \begin_layout Plain Layout - case UBranch(func) => if (maxDepth == 0) Leaf(default) else { +z1 \end_layout +\end_inset + + and +\begin_inset listings +inline true +status open + \begin_layout Plain Layout - val (z1, z2) = func(()) +z2 \end_layout +\end_inset + + should set +\begin_inset listings +inline true +status open + \begin_layout Plain Layout - Branch(toT2(maxDepth - 1, default)(z1), toT2(maxDepth - 1, default)(z2)) +makeLeaf \end_layout +\end_inset + + depending on +\begin_inset listings +inline true +status open + \begin_layout Plain Layout - } +startAt \end_layout +\end_inset + + being odd or even. + Looking at the examples, we find that +\begin_inset listings +inline true +status open + \begin_layout Plain Layout -} +f \end_layout \end_inset -To test this code, let us truncate the infinite structure + must also return a \begin_inset listings inline true status open \begin_layout Plain Layout -tree1toInf +Left() \end_layout \end_inset - at depth -\begin_inset Formula $4$ + when +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +startAt == 0 +\end_layout + \end_inset . - The result is a finite tree of type + So, the code of \begin_inset listings inline true status open \begin_layout Plain Layout -T2[Int] +f \end_layout \end_inset -, where the unevaluated part of the tree is shown as -\begin_inset Quotes eld -\end_inset - - + is: \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout --1 +val f: Z => Either[Int, (Z, Z)] = { +\end_layout + +\begin_layout Plain Layout + + case Z(n, false) if n > 0 && n % 2 == 0 => +\end_layout + +\begin_layout Plain Layout + + Right((Z(n - 1, false), Z(n, true))) +\end_layout + +\begin_layout Plain Layout + + case Z(n, false) if n > 0 && n % 2 == 1 => +\end_layout + +\begin_layout Plain Layout + + Right((Z(n, true), Z(n - 1, false))) +\end_layout + +\begin_layout Plain Layout + + case Z(n, _) => Left(n) // Make a leaf when + n == 0 or makeLeaf == true. +\end_layout + +\begin_layout Plain Layout + +} \end_layout \end_inset +The implementation of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +evenOdd +\end_layout -\begin_inset Quotes erd \end_inset -: + is now complete. + To test: \end_layout \begin_layout Standard @@ -13257,7 +13496,7 @@ status open lines 0 placement l overhang 0in -width "82col%" +width "84col%" status open \begin_layout Plain Layout @@ -13271,13 +13510,12 @@ status open \begin_layout Plain Layout -scala> toT2(maxDepth = 3, default = -1)(tree1toInf) +scala> evenOdd(3) \end_layout \begin_layout Plain Layout -res0: T2[Int] = Branch(Leaf(1), Branch(Leaf(2), Branch(Leaf(3), Branch(Leaf(4), - Leaf(-1))))) +res0: T2[Int] = Branch(Leaf(3), Branch(Branch(Leaf(1), Leaf(0)), Leaf(2))) \end_layout \end_inset @@ -13286,7 +13524,7 @@ res0: T2[Int] = Branch(Leaf(1), Branch(Leaf(2), Branch(Leaf(3), Branch(Leaf(4), \end_layout \begin_layout Plain Layout -\begin_inset VSpace -50baselineskip% +\begin_inset VSpace 0baselineskip% \end_inset @@ -13299,10 +13537,10 @@ res0: T2[Int] = Branch(Leaf(1), Branch(Leaf(2), Branch(Leaf(3), Branch(Leaf(4), \begin_layout Standard \noindent -\begin_inset space ~ +\begin_inset VSpace 30baselineskip% \end_inset - + \begin_inset Preview \begin_layout Standard @@ -13315,7 +13553,7 @@ status open \backslash -Tree[ 1 [ 2 [3 [ 4 -1 ] ] ] ] +Tree[ 3 [ [ 1 0 ] 2 ] ] \end_layout \end_inset @@ -13326,401 +13564,355 @@ Tree[ 1 [ 2 [3 [ 4 -1 ] ] ] ] \end_inset -\end_layout +\begin_inset Newline newline +\end_inset + -\begin_layout Subsection -Recursion schemes. - III. - Traversing operations \end_layout \begin_layout Standard -Folding with a recursion scheme ( -\begin_inset Formula $\text{fold}_{S}$ +\noindent +\begin_inset Formula $\square$ \end_inset -) allows us to implement operations such as -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout -printLaTeXSubtree \end_layout -\end_inset - - (Section +\begin_layout Standard +The kind of reasoning shown in Examples \begin_inset space ~ \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:Recursion-schemes.-folding" +reference "subsec:Example-unfold-list" plural "false" caps "false" noprefix "false" \end_inset -) that cannot be expressed via ordinary -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -fold -\end_layout +– +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Example-unfold-tree-evenodd" +plural "false" +caps "false" +noprefix "false" \end_inset - or -\begin_inset listings -inline true + is known as reasoning by +\series bold +co-induction +\series default + +\begin_inset Index idx status open \begin_layout Plain Layout - -traverse +co-induction \end_layout \end_inset - functions. - Another operation not expressible via +. + It is related to mathematical induction but is significantly different + from the reasoning required to write the code for a folding operation (which + is directly modeled on induction). + Note that \begin_inset listings inline true status open \begin_layout Plain Layout -traverse +unfold(f)(z) \end_layout \end_inset - is -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -zipWithDepth -\end_layout - + will call itself whenever the value +\begin_inset Formula $f(z)$ \end_inset -, which we implemented in Section -\begin_inset space ~ + of type +\begin_inset Formula $S^{A,Z}$ \end_inset + contains additional values of type +\begin_inset Formula $Z$ +\end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Tasks-not-implementable-via-traverse" -plural "false" -caps "false" -noprefix "false" +. + The programmer must carefully choose a suitable type +\begin_inset Formula $Z$ +\end_inset + and a suitable function +\begin_inset Formula $f$ \end_inset - through custom code. - We will now implement + such that \begin_inset listings inline true status open \begin_layout Plain Layout -zipWithDepth +unfold(f)(z) \end_layout \end_inset - via a more general traversal operation ( -\begin_inset Formula $\text{trav}_{S}$ + stops the recursion at the required places. + For instance, if +\begin_inset Formula $S^{A,Z}\triangleq A+Z\times Z$ \end_inset -) parameterized by an arbitrary recursion scheme -\begin_inset Formula $S$ +, the function +\begin_inset Formula $f$ \end_inset - and an arbitrary functor -\begin_inset Formula $F$ + must sometimes return a value of type +\begin_inset Formula $A+\bbnum 0$ \end_inset - (not necessarily applicative!). -\end_layout + to stop the unfolding. + One could say that the base cases in co-induction are not at the beginning + of the computation but +\begin_inset Quotes eld +\end_inset -\begin_layout Standard -To figure out the type signature of -\begin_inset Formula $\text{trav}_{S}$ +in the future +\begin_inset Quotes erd \end_inset -, consider the relationship between +. + +\end_layout + +\begin_layout Standard +Is the recursion guaranteed to stop while evaluating \begin_inset listings inline true status open \begin_layout Plain Layout -foldMap +unfold \end_layout \end_inset -, -\begin_inset Formula $\text{fold}_{S}$ +? The type signature +\begin_inset Formula $f:Z\rightarrow S^{A,Z}$ \end_inset -, and the ordinary -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -traverse -\end_layout + itself does not guarantee that +\begin_inset Formula $f(z)$ +\end_inset + returns values that will stop the unfolding at the right places. + If +\begin_inset Formula $S^{A,Z}\triangleq A+Z\times Z$ \end_inset - (denoted -\begin_inset Formula $\text{trav}_{L}$ + and +\begin_inset Formula $f(z)$ \end_inset -): -\begin_inset Formula -\begin{align*} - & \text{foldMap}_{L}(f^{:A\rightarrow Z}):L^{A}\rightarrow Z\quad,\quad\quad\text{trav}_{L}(f^{:A\rightarrow F^{B}}):L^{A}\rightarrow F^{L^{B}}\quad,\\ - & \text{fold}_{S}(f^{:S^{A,Z}\rightarrow Z}):L^{A}\rightarrow Z\quad,\quad\quad\text{trav}_{S}(f^{:???}):L^{A}\rightarrow F^{L^{B}}\quad. -\end{align*} + always returns values of type +\begin_inset Formula $\bbnum 0+Z\times Z$ +\end_inset + (for example, +\begin_inset Formula $f(z)\triangleq\bbnum 0+z\times z$ \end_inset -We recover +), the unfolding operation \begin_inset listings inline true status open \begin_layout Plain Layout -foldMap +unfold(f) \end_layout \end_inset - from -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -traverse + will enter an infinite loop trying to construct a tree of infinite size. + This will, of course, fail since data structures in a computer cannot have + infinite size. \end_layout +\begin_layout Standard +Unfolding will always terminate if we use a data type that +\emph on +delays +\emph default + the evaluation of its recursively defined parts. + Those parts will be computed only on demand. + To obtain further data, the code needs to call certain functions. + Data types of this kind are sometimes called +\begin_inset Quotes eld \end_inset - by setting the applicative functor -\begin_inset Formula $F$ +infinite +\begin_inset Quotes erd \end_inset - as -\begin_inset Formula $F^{B}\triangleq Z$ -\end_inset +, +\begin_inset Index idx +status open -. - So, we expect to obtain the type signature of -\begin_inset Formula $\text{trav}_{S}$ -\end_inset +\begin_layout Plain Layout +infinite data types +\end_layout - from the type signature of -\begin_inset Formula $\text{fold}_{S}$ \end_inset - if we replace -\begin_inset Formula $Z$ -\end_inset + which is misleading since only a finite amount of data is ever stored in + memory. +\end_layout - by -\begin_inset Formula $F^{L^{B}}$ +\begin_layout Standard +As an example, consider the recursion scheme +\begin_inset Formula $S^{A,R}\triangleq A+(\bbnum 1\rightarrow R\times R)$ \end_inset . - The first argument -\begin_inset Formula $f$ + The corresponding data type +\begin_inset Formula $L^{A}\triangleq S^{A,L^{A}}$ \end_inset - of -\begin_inset Formula $\text{trav}_{S}$ + is a binary tree whose branches are evaluated on demand (but leaves are + evaluated eagerly). + This data structure supports unfolding with +\emph on +any +\emph default + function +\begin_inset Formula $f:Z\rightarrow S^{A,Z}$ \end_inset - will then have the type -\begin_inset Formula $S^{A,F^{L^{B}}}\rightarrow F^{L^{B}}$ + because the recursive evaluation of +\begin_inset Formula $f$ \end_inset -: -\begin_inset Formula -\[ -\text{trav}_{S}:(S^{A,F^{L^{B}}}\rightarrow F^{L^{B}})\rightarrow L^{A}\rightarrow F^{L^{B}}\quad. -\] + at the branches is always delayed. + For instance, +\begin_inset listings +inline true +status open -\end_inset +\begin_layout Plain Layout -To implement this method, begin with the type equivalence -\begin_inset Formula $L^{A}\cong S^{A,L^{A}}$ -\end_inset +unfold +\end_layout -. - We can apply -\begin_inset Formula $\text{trav}_{S}(f)$ \end_inset - recursively to the values of type + can generate a value of type \begin_inset Formula $L^{A}$ \end_inset - stored inside -\begin_inset Formula $S^{A,L^{A}}$ -\end_inset - - and obtain a value of type -\begin_inset Formula $S^{A,F^{L^{B}}}$ -\end_inset + whose tree structure has the form +\begin_inset Preview -: -\begin_inset Formula -\[ -\big(s^{:S^{A,L^{A}}}\triangleright\,\overline{\text{trav}_{S}(f)}^{\uparrow S^{A,\bullet}}\big):S^{A,F^{L^{B}}}\quad. -\] +\begin_layout Standard -\end_inset +\size tiny +\begin_inset ERT +status open -It remains to apply -\begin_inset Formula $f$ -\end_inset +\begin_layout Plain Layout - to the last obtained value. - This completes the code of -\begin_inset Formula $\text{trav}_{S}$ -\end_inset -: -\begin_inset Formula -\[ -\text{trav}_{S}\big(f^{:S^{A,F^{L^{B}}}\rightarrow F^{L^{B}}}\big)\triangleq\overline{\text{trav}_{S}(f)}^{\uparrow S^{A,\bullet}}\bef f\quad. -\] +\backslash +Tree[ 1 [ 2 [3 ... + ] ] ] +\end_layout \end_inset \end_layout -\begin_layout Standard -The method -\begin_inset Formula $\text{trav}_{S}$ -\end_inset - - works in the same way for all recursion schemes -\begin_inset Formula $S$ \end_inset - and for all type constructors -\begin_inset Formula $F$ + with unbounded size: +\begin_inset VSpace 60baselineskip% \end_inset -. - This makes -\begin_inset Formula $\text{trav}_{S}$ -\end_inset - powerful but hard to use because we need to work with data structures defined - via the \begin_inset listings -inline true +inline false status open \begin_layout Plain Layout -Fix +type S[A, R] = Either[A, Unit => (R, R)] \end_layout -\end_inset - - type constructor. - It is easier to use a specialized version of -\begin_inset Formula $\text{trav}_{S}$ -\end_inset +\begin_layout Plain Layout - for the data structure at hand, similarly to what we did in the previous - sections for folding and unfolding. - \end_layout -\begin_layout Standard -To illustrate this, let us implement -\begin_inset listings -inline true -status open - \begin_layout Plain Layout -zipWithDepth +sealed trait UT[A] // A tree with on-call evaluation of branches and + eager evaluation of leaves. \end_layout -\end_inset +\begin_layout Plain Layout - via -\begin_inset Formula $\text{trav}_{S}$ -\end_inset +case class ULeaf[A](a: A) extends UT[A] +\end_layout - with a binary tree recursion scheme. - We will use the -\begin_inset listings -inline true -status open +\begin_layout Plain Layout + +case class UBranch[A](run: Unit => (UT[A], UT[A])) extends UT[A] // Call + run(()) to get the branches. +\end_layout \begin_layout Plain Layout -State \end_layout -\end_inset +\begin_layout Plain Layout - monad as the functor -\begin_inset Formula $F$ -\end_inset +def unfoldUT[A, Z](f: Z => S[A, Z])(init: Z): UT[A] = f(init) match { +\end_layout -: -\begin_inset listings -inline false -status open +\begin_layout Plain Layout + + case Left(a) => ULeaf(a) +\end_layout \begin_layout Plain Layout -final case class St[A](run: Int => (A, Int)) { // A State monad with internal - state of type Int. + case Right(func) => UBranch { _ => // It is important to delay the + evaluation of func(()). \end_layout \begin_layout Plain Layout - import io.chymyst.ch.implement // Derive these methods automatical -ly from types. + val (z1, z2) = func(()) // Force the evaluation of branches + at this level. \end_layout \begin_layout Plain Layout - def flatMap[B](f: A => St[B]): St[B] = implement + (unfoldUT(f)(z1), unfoldUT(f)(z2)) // `unfoldUT` will delay the + evaluation of further branches. \end_layout \begin_layout Plain Layout - def map[B](f: A => B): St[B] = implement + } \end_layout \begin_layout Plain Layout @@ -13730,217 +13922,179 @@ ly from types. \begin_layout Plain Layout -def incrementAndGet: St[Int] = St(s => (s + 1, s + 1)) // Increment the - current state value. \end_layout \begin_layout Plain Layout -def get: St[Int] = St(s => (s, s)) // Fetch the current state - value. +val tree1toInf = unfoldUT[Int, (Int, Boolean)] { case (z, makeLeaf) => \end_layout \begin_layout Plain Layout -def set(s: Int): St[Unit] = St(_ => ((), s)) // Set the state, ignore - previous state value. + if (makeLeaf) Left(z) else Right(_ => ((z + 1, true), (z + 1, false))) \end_layout -\end_inset +\begin_layout Plain Layout -Next, define the recursion scheme -\begin_inset Formula $S^{A,R}$ -\end_inset +}((0, false)) +\end_layout - and a specialized version of -\begin_inset Formula $\text{trav}_{S}$ \end_inset - for trees of type +The value \begin_inset listings inline true status open \begin_layout Plain Layout -T2[A] +tree1toInf \end_layout \end_inset -: + is finite but can compute a tree of unbounded depth. + To visualize \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -type S[A, R] = Either[A, (R, R)] // Recursion scheme for T2[A]. -\end_layout - -\begin_layout Plain Layout - -def travT2[A, B, F[_]](f: S[A, F[T2[B]]] => F[T2[B]]): T2[A] => F[T2[B]] - = { -\end_layout - -\begin_layout Plain Layout - - case Leaf(a) => f(Left(a)) +tree1toInf \end_layout -\begin_layout Plain Layout +\end_inset - case Branch(l, r) => f(Right((travT2(f)(l), travT2(f)(r)))) -\end_layout +, we write a function that converts +\begin_inset listings +inline true +status open \begin_layout Plain Layout -} +UT[A] \end_layout \end_inset -It remains to apply + to \begin_inset listings inline true status open \begin_layout Plain Layout -travT2 +T2[A] \end_layout \end_inset - to a suitable function + by stopping at a given maximum depth. + The unevaluated parts of the tree will be marked with a value called \begin_inset listings inline true status open \begin_layout Plain Layout -f +default \end_layout \end_inset -. - The value of the internal state will represent the current depth of the - tree element. - We need to increment the depth whenever we find a branch and then traverse - the two subtrees starting from the same depth value. - The code is: +: \begin_inset listings inline false status open \begin_layout Plain Layout -def zipWithDepth[A](tree: T2[A]): T2[(A, Int)] = travS[A, (A, Int), St] - { -\end_layout - -\begin_layout Plain Layout - - case Left(a) => for { s <- get } yield Leaf((a, s)) // Put the current - depth into the Leaf value. -\end_layout - -\begin_layout Plain Layout - - case Right((l, r)) => for { +def toT2[A](maxDepth: Int, default: A): UT[A] => T2[A] = { \end_layout \begin_layout Plain Layout - s <- incrementAndGet // Read the current depth after incrementing - it. + case ULeaf(a) => Leaf(a)e \end_layout \begin_layout Plain Layout - x <- l // Traverse the left branch starting from depth - `s`. + case UBranch(func) => if (maxDepth == 0) Leaf(default) else { \end_layout \begin_layout Plain Layout - _ <- set(s) // Set the same initial depth `s` for traversing - the right branch. - + val (z1, z2) = func(()) \end_layout \begin_layout Plain Layout - y <- r // Traverse the right branch. + Branch(toT2(maxDepth - 1, default)(z1), toT2(maxDepth - 1, default)(z2)) \end_layout \begin_layout Plain Layout - } yield Branch(x, y) + } \end_layout \begin_layout Plain Layout -}(tree).run(0)._1 +} \end_layout \end_inset -To test the code, apply +To test this code, let us truncate the infinite structure \begin_inset listings inline true status open \begin_layout Plain Layout -zipWithDepth +tree1toInf \end_layout \end_inset - to a sample tree + at depth +\begin_inset Formula $4$ +\end_inset + +. + The result is a finite tree of type \begin_inset listings inline true status open \begin_layout Plain Layout -t2 +T2[Int] \end_layout \end_inset - used earlier in Sections -\begin_inset space ~ +, where the unevaluated part of the tree is shown as +\begin_inset Quotes eld \end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Decorating-a-tree-breadth-first-traversal" -plural "false" -caps "false" -noprefix "false" +\begin_inset listings +inline true +status open -\end_inset +\begin_layout Plain Layout - and -\begin_inset space ~ -\end_inset +-1 +\end_layout +\end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Tasks-not-implementable-via-traverse" -plural "false" -caps "false" -noprefix "false" +\begin_inset Quotes erd \end_inset -: +: \end_layout \begin_layout Standard @@ -13948,11 +14102,11 @@ noprefix "false" lines 0 placement l overhang 0in -width "78col%" +width "82col%" status open \begin_layout Plain Layout -\begin_inset VSpace -75baselineskip% +\begin_inset VSpace -95baselineskip% \end_inset @@ -13962,13 +14116,13 @@ status open \begin_layout Plain Layout -scala> zipWithDepth(t2) +scala> toT2(maxDepth = 3, default = -1)(tree1toInf) \end_layout \begin_layout Plain Layout -res0: T2[(Int, Int)] = Branch(Leaf((8, 1)), Branch(Branch(Leaf((3, 3)), - Leaf((5, 3))), Leaf((4, 2)))) +res0: T2[Int] = Branch(Leaf(1), Branch(Leaf(2), Branch(Leaf(3), Branch(Leaf(4), + Leaf(-1))))) \end_layout \end_inset @@ -14006,7 +14160,7 @@ status open \backslash -Tree[ (8,1) [ [ (3,3) (5,3) ] (4,2) ] ] +Tree[ 1 [ 2 [3 [ 4 -1 ] ] ] ] \end_layout \end_inset @@ -14019,155 +14173,76 @@ Tree[ (8,1) [ [ (3,3) (5,3) ] (4,2) ] ] \end_layout -\begin_layout Standard -\noindent -\begin_inset space ~ +\begin_layout Subsection +Exercises +\begin_inset Index idx +status open + +\begin_layout Plain Layout +exercises +\end_layout + \end_inset \end_layout -\begin_layout Standard -Here we need to use the -\begin_inset listings -inline true -status open +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:Exercise-traversables-7-1" -\begin_layout Plain Layout +\end_inset -State -\end_layout + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Exercise-traversables-7-1" +plural "false" +caps "false" +noprefix "false" \end_inset - monad's method + +\end_layout + +\begin_layout Standard +Implement \begin_inset listings inline true status open \begin_layout Plain Layout -set(s) +foldMap \end_layout \end_inset - with a value + and \begin_inset listings inline true status open \begin_layout Plain Layout -s +traverse \end_layout \end_inset - obtained from a previous monadic computation. - So, the code involves -\begin_inset Formula $\text{trav}_{S}(f)$ -\end_inset + for the following type constructors: +\end_layout - with a function -\begin_inset Formula $f:S^{A,F^{L^{B}}}\rightarrow F^{L^{B}}$ -\end_inset +\begin_layout Standard - whose -\begin_inset Formula $F$ -\end_inset - --effect is -\emph on -not -\emph default - equivalent to an applicative functor's effect (which cannot depend on a - previously computed value). - We can see that the recursion scheme-based traversal ( -\begin_inset Formula $\text{trav}_{S}$ -\end_inset - -) is more powerful than the plain traversal, -\begin_inset Formula $\text{trav}_{L}(f^{:A\rightarrow F^{B}})$ -\end_inset - -, that may only use -\begin_inset Formula $F$ -\end_inset - -'s applicative functor methods. -\end_layout - -\begin_layout Subsection -Exercises -\begin_inset Index idx -status open - -\begin_layout Plain Layout -exercises -\end_layout - -\end_inset - - -\end_layout - -\begin_layout Subsubsection -Exercise -\begin_inset CommandInset label -LatexCommand label -name "subsec:Exercise-traversables-7-1" - -\end_inset - - -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Exercise-traversables-7-1" -plural "false" -caps "false" -noprefix "false" - -\end_inset - - -\end_layout - -\begin_layout Standard -Implement -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -foldMap -\end_layout - -\end_inset - - and -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -traverse -\end_layout - -\end_inset - - for the following type constructors: -\end_layout - -\begin_layout Standard - -\series bold -(a) -\series default - -\begin_inset Formula $F^{A}\triangleq\text{Int}+A+A\times A\quad.$ +\series bold +(a) +\series default + +\begin_inset Formula $F^{A}\triangleq\text{Int}+A+A\times A\quad.$ \end_inset @@ -27742,80 +27817,1164 @@ noprefix "false" \end_inset - + +\end_layout + +\begin_layout Standard +Given a monoid type +\begin_inset Formula $R$ +\end_inset + + and a +\emph on +monad +\emph default + morphism +\begin_inset Formula $\phi:M\leadsto N$ +\end_inset + + between some monads +\begin_inset Formula $M$ +\end_inset + + and +\begin_inset Formula $N$ +\end_inset + +, prove that +\begin_inset Formula $\phi:M^{R}\rightarrow N^{R}$ +\end_inset + + is a monoid morphism between +\begin_inset Formula $M^{R}$ +\end_inset + + and +\begin_inset Formula $N^{R}$ +\end_inset + +. + (The types +\begin_inset Formula $M^{R}$ +\end_inset + + and +\begin_inset Formula $N^{R}$ +\end_inset + + are monoids due to Exercise +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Exercise-monad-of-monoid-is-monoid" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +). +\end_layout + +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:Exercise-traversables-10-1-1-1" + +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Exercise-traversables-10-1-1-1" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + +\end_layout + +\begin_layout Standard + +\series bold +(a) +\series default + Each monad has at the same time an applicative functor instance. + Given a monad morphism +\begin_inset Formula $\phi:M\leadsto N$ +\end_inset + + between two monads, show that +\begin_inset Formula $\phi$ +\end_inset + + is also an applicative morphism between applicative functors +\begin_inset Formula $M$ +\end_inset + + and +\begin_inset Formula $N$ +\end_inset + +. +\end_layout + +\begin_layout Standard + +\series bold +(b) +\series default + Show that the converse does not hold: if +\begin_inset Formula $\phi:M\leadsto N$ +\end_inset + + is an applicative morphism between two monads then +\begin_inset Formula $\phi$ +\end_inset + + is +\emph on +not +\emph default + necessarily a monad morphism. +\end_layout + +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:Exercise-traversables-10-3" + +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Exercise-traversables-10-3" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + +\end_layout + +\begin_layout Standard +Given an applicative functor +\begin_inset Formula $M$ +\end_inset + + and a monoid morphism +\begin_inset Formula $\phi:R\rightarrow S$ +\end_inset + + between some monoid types +\begin_inset Formula $R$ +\end_inset + + and +\begin_inset Formula $S$ +\end_inset + +, prove that +\begin_inset Formula $\phi^{\uparrow M}:M^{R}\rightarrow M^{S}$ +\end_inset + + is also a monoid morphism. + (The types +\begin_inset Formula $M^{R}$ +\end_inset + + and +\begin_inset Formula $M^{S}$ +\end_inset + + are monoids due to Exercise +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Exercise-applicative-of-monoid-is-monoid" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +). +\end_layout + +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:Exercise-traversables-10-1-1" + +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Exercise-traversables-10-1-1" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + +\end_layout + +\begin_layout Standard +Given a monoid type +\begin_inset Formula $R$ +\end_inset + + and an applicative morphism +\begin_inset Formula $\phi:M\leadsto N$ +\end_inset + + between some applicative functors +\begin_inset Formula $M$ +\end_inset + + and +\begin_inset Formula $N$ +\end_inset + +, prove that +\begin_inset Formula $\phi:M^{R}\rightarrow N^{R}$ +\end_inset + + is a monoid morphism between +\begin_inset Formula $M^{R}$ +\end_inset + + and +\begin_inset Formula $N^{R}$ +\end_inset + +. + (The types +\begin_inset Formula $M^{R}$ +\end_inset + + and +\begin_inset Formula $N^{R}$ +\end_inset + + are monoids due to Exercise +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Exercise-applicative-of-monoid-is-monoid" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +). +\end_layout + +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:Exercise-traversables-10-3-1" + +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Exercise-traversables-10-3-1" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + +\end_layout + +\begin_layout Standard +Given a monoid +\begin_inset Formula $M$ +\end_inset + +, define the functions +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +inMF +\end_layout + +\end_inset + + and +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +outMF +\end_layout + +\end_inset + +: +\begin_inset Formula +\begin{align*} + & \text{inMF}:M\rightarrow M\rightarrow M\quad,\quad\quad\text{inMF}\,(x^{:M})\triangleq y^{:M}\rightarrow x\oplus y\quad,\\ + & \text{outMF}:(M\rightarrow M)\rightarrow M\quad,\quad\quad\text{outMF}\,(p^{:M\rightarrow M})\triangleq p(e_{M})\quad. +\end{align*} + +\end_inset + +This definition of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +inMF +\end_layout + +\end_inset + + is similar to that used in the proof of Statement +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Statement-foldleft-foldmap-equivalence" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +(c). +\end_layout + +\begin_layout Standard + +\series bold +(a) +\series default + Prove that +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +inMF +\end_layout + +\end_inset + + is a monoid morphism between +\begin_inset Formula $M$ +\end_inset + + and the monoid +\begin_inset Formula $\text{MF}^{M}$ +\end_inset + + consisting of all functions of type +\begin_inset Formula $M\rightarrow M$ +\end_inset + +. + Define the empty element and the binary operation of +\begin_inset Formula $\text{MF}^{M}$ +\end_inset + + appropriately. +\end_layout + +\begin_layout Standard + +\series bold +(b) +\series default + Prove that +\begin_inset Formula $\text{inMF}\bef\text{outMF}=\text{id}$ +\end_inset + + but +\begin_inset Formula $\text{outMF}\bef\text{inMF}\neq\text{id}$ +\end_inset + +. + (Give an example of a monoid +\begin_inset Formula $M$ +\end_inset + + where the second equation does not hold.) +\end_layout + +\begin_layout Standard + +\series bold +(c) +\series default + Prove that +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +outMF +\end_layout + +\end_inset + + is +\emph on +not +\emph default + a monoid morphism between +\begin_inset Formula $\text{MF}^{M}$ +\end_inset + + and +\begin_inset Formula $M$ +\end_inset + +. +\end_layout + +\begin_layout Subsubsection +Exercise +\begin_inset CommandInset label +LatexCommand label +name "subsec:Exercise-traversables-10-1-1-2" + +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Exercise-traversables-10-1-1-2" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + +\end_layout + +\begin_layout Standard +For any applicative functor +\begin_inset Formula $F$ +\end_inset + + with a known +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +zip +\end_layout + +\end_inset + + method, define the +\begin_inset Quotes eld +\end_inset + +reversed +\begin_inset Quotes erd +\end_inset + + +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +zip +\end_layout + +\end_inset + + method as: +\begin_inset Formula +\[ +\text{zip}_{\text{rev}F}:F^{A}\times F^{B}\rightarrow F^{A\times B}\quad,\quad\quad\text{zip}_{\text{rev}F}\triangleq\text{swap}\bef\text{zip}_{F}\bef\text{swap}^{\uparrow F}\quad. +\] + +\end_inset + + +\end_layout + +\begin_layout Standard + +\series bold +(a) +\series default + Show that +\begin_inset Formula $\text{zip}_{\text{rev}F}$ +\end_inset + + also provides a lawful applicative instance for +\begin_inset Formula $F$ +\end_inset + + (where +\begin_inset Formula $F$ +\end_inset + +'s +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +pure +\end_layout + +\end_inset + + method remains unchanged). + Show that +\begin_inset Formula $\text{zip}_{\text{rev}F}=\text{zip}_{F}$ +\end_inset + + whenever +\begin_inset Formula $F$ +\end_inset + + is commutative. +\end_layout + +\begin_layout Standard + +\series bold +(b) +\series default + Show that the +\begin_inset Quotes eld +\end_inset + +applicative reversal +\begin_inset Quotes erd +\end_inset + + obeys an applicative naturality law: for any applicative functors +\begin_inset Formula $F$ +\end_inset + +, +\begin_inset Formula $G$ +\end_inset + + and an applicative morphism +\begin_inset Formula $\phi:F^{A}\rightarrow G^{A}$ +\end_inset + +, the following equation holds: +\begin_inset Formula +\[ +(p^{:F^{A}}\times q^{:F^{B}})\triangleright\text{zip}_{\text{rev}F}\triangleright\phi=(\phi(p)\times\phi(q))\triangleright\text{zip}_{\text{rev}G}\quad. +\] + +\end_inset + + +\end_layout + +\begin_layout Section +Discussion and further developments +\end_layout + +\begin_layout Subsection +Recursion schemes and structure-preserving maps +\end_layout + +\begin_layout Standard +Sections +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Recursion-schemes.-folding" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + and +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:Recursion-schemes.-II.unfolding" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + show the general +\begin_inset Quotes eld +\end_inset + +folding +\begin_inset Quotes erd +\end_inset + + and +\begin_inset Quotes eld +\end_inset + +unfolding +\begin_inset Quotes erd +\end_inset + + operations that work in the same way for all recursion schemes. + It turns out that those operations are mathematically unique. + We will now prove that there is only one +\begin_inset Quotes eld +\end_inset + +folding +\begin_inset Quotes erd +\end_inset + + function and only one +\begin_inset Quotes eld +\end_inset + +unfolding +\begin_inset Quotes erd +\end_inset + + function obeying appropriate laws. + +\end_layout + +\begin_layout Standard +What are those laws? The +\begin_inset Quotes eld +\end_inset + +folding +\begin_inset Quotes erd +\end_inset + + function had the type signature: +\begin_inset Formula +\[ +\text{fold}_{S}:(S^{A,Z}\rightarrow Z)\rightarrow L^{A}\rightarrow Z\quad. +\] + +\end_inset + +Given +\begin_inset Formula $f:S^{A,Z}\rightarrow Z$ +\end_inset + +, the function +\begin_inset Formula $\text{fold}_{S}(f)$ +\end_inset + + transforms a value of type +\begin_inset Formula $L^{A}$ +\end_inset + + into a value of type +\begin_inset Formula $Z$ +\end_inset + +. + Here, the type +\begin_inset Formula $L^{A}$ +\end_inset + + is defined recursively via the equation +\begin_inset Formula $L^{A}\triangleq S^{A,L^{A}}$ +\end_inset + + while the type +\begin_inset Formula $Z$ +\end_inset + + is arbitrary. + +\end_layout + +\begin_layout Standard +It is actually not so important that +\begin_inset Formula $L^{A}$ +\end_inset + + is a data structure with a type parameter, because the code of +\begin_inset Formula $\text{fold}_{S}$ +\end_inset + + always keeps +\begin_inset Formula $A$ +\end_inset + + fixed. + So, we may simplify our considerations by eliminating the type parameter + +\begin_inset Formula $A$ +\end_inset + +. + We replace the recursion scheme +\begin_inset Formula $S^{A,R}$ +\end_inset + + by just +\begin_inset Formula $S^{R}$ +\end_inset + + (where +\begin_inset Formula $S$ +\end_inset + + is a functor) and the type +\begin_inset Formula $L^{A}$ +\end_inset + + by a type +\begin_inset Formula $T$ +\end_inset + + defined by the recursive equation +\begin_inset Formula $T\triangleq S^{T}$ +\end_inset + +. + Then the type of +\begin_inset Formula $\text{fold}_{S}$ +\end_inset + + is simplified to: +\begin_inset Formula +\[ +\text{fold}_{S}:(S^{Z}\rightarrow Z)\rightarrow T\rightarrow Z\quad. +\] + +\end_inset + + +\end_layout + +\begin_layout Standard +At first sight, the types +\begin_inset Formula $T$ +\end_inset + + and +\begin_inset Formula $Z$ +\end_inset + + have nothing in common. + However, we need to keep in mind that +\begin_inset Formula $T$ +\end_inset + + is equivalent to +\begin_inset Formula $S^{T}$ +\end_inset + + by definition of the recursive type +\begin_inset Formula $T$ +\end_inset + +. + The equivalent types +\begin_inset Formula $T$ +\end_inset + + and +\begin_inset Formula $S^{T}$ +\end_inset + + are related by a pair of isomorphisms that we may call +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +fix +\end_layout + +\end_inset + + and +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +unfix +\end_layout + +\end_inset + +: +\begin_inset Formula +\[ +\text{fix}:S^{T}\rightarrow T\quad,\quad\text{unfix}:T\rightarrow S^{T}\quad,\quad\text{fix}\bef\text{unfix}=\text{id}\quad,\quad\text{unfix}\bef\text{fix}=\text{id}\quad. +\] + +\end_inset + +We consider the functions +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +fix +\end_layout + +\end_inset + + and +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +unfix +\end_layout + +\end_inset + + as given. + The type signature of +\begin_inset Formula $\text{fold}_{S}$ +\end_inset + + requires a function of type +\begin_inset Formula $S^{Z}\rightarrow Z$ +\end_inset + +, which is similar to the type of +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +fix +\end_layout + +\end_inset + +. + So, inside the function body of +\begin_inset Formula $\text{fold}_{S}(f)$ +\end_inset + +, we have types +\begin_inset Formula $T$ +\end_inset + + and +\begin_inset Formula $Z$ +\end_inset + + for which functions of types +\begin_inset Formula $S^{T}\rightarrow T$ +\end_inset + + and +\begin_inset Formula $S^{Z}\rightarrow Z$ +\end_inset + + are available. +\end_layout + +\begin_layout Standard +It is useful at this point to borrow some terminology from category theory + where an object +\begin_inset Formula $Z$ +\end_inset + + with a morphism of type +\begin_inset Formula $S^{Z}\rightarrow Z$ +\end_inset + + has a special name. + The object +\begin_inset Formula $Z$ +\end_inset + + is called a +\series bold +functor algebra +\series default + on the functor +\begin_inset Formula $S$ +\end_inset + +, and +\begin_inset Index idx +status open + +\begin_layout Plain Layout +functor algebra +\end_layout + +\end_inset + + the morphism of type +\begin_inset Formula $S^{Z}\rightarrow Z$ +\end_inset + + is called the algebra's +\series bold +structure map +\series default +. +\begin_inset Index idx +status open + +\begin_layout Plain Layout +functor algebra!structure map +\end_layout + +\end_inset + + +\begin_inset Foot +status open + +\begin_layout Plain Layout +In the mathematical literature, a functor algebra on a functor +\begin_inset Formula $F$ +\end_inset + + is commonly referred to as an +\begin_inset Formula $F$ +\end_inset + + +\series bold +-algebra +\series default + +\begin_inset Index idx +status open + +\begin_layout Plain Layout +\begin_inset Formula $F$ +\end_inset + +-algebra!see functor algebra +\end_layout + +\end_inset + +. + That terminology is rather inconvenient because the relevant functor is + not always called +\begin_inset Formula $F$ +\end_inset + +. +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +From a programmer's viewpoint, an intuitive picture is that the type +\begin_inset Formula $Z$ +\end_inset + + must be able somehow to absorb information from the type +\begin_inset Formula $S^{Z}$ +\end_inset + +. + For instance, if +\begin_inset Formula $S$ +\end_inset + + is a recursion scheme for a binary tree (such as, +\begin_inset Formula $S^{R}=A+R\times R$ +\end_inset + + for +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +T2 +\end_layout + +\end_inset + +) then the type +\begin_inset Formula $S^{Z}\rightarrow Z$ +\end_inset + + is equivalent to the product type +\begin_inset Formula $(A\rightarrow Z)\times(Z\times Z\rightarrow Z)$ +\end_inset + +. + So, +\begin_inset Formula $Z$ +\end_inset + + must support an operation that maps a tree leaf (of some fixed type +\begin_inset Formula $A$ +\end_inset + +) into a value of type +\begin_inset Formula $Z$ +\end_inset + +, and also an operation that maps two tree branches (both of type +\begin_inset Formula $Z$ +\end_inset + +) into another value of type +\begin_inset Formula $Z$ +\end_inset + +. + Those operations are automatically +\begin_inset Quotes eld +\end_inset + +induced +\begin_inset Quotes erd +\end_inset + + by the recursion scheme +\begin_inset Formula $S$ +\end_inset + +. + The type +\begin_inset Formula $T$ +\end_inset + + must also support the same operations. \end_layout \begin_layout Standard -Given a monoid type -\begin_inset Formula $R$ -\end_inset - - and a -\emph on -monad -\emph default - morphism -\begin_inset Formula $\phi:M\leadsto N$ -\end_inset - - between some monads -\begin_inset Formula $M$ +So, both types +\begin_inset Formula $T$ \end_inset and -\begin_inset Formula $N$ +\begin_inset Formula $Z$ \end_inset -, prove that -\begin_inset Formula $\phi:M^{R}\rightarrow N^{R}$ + have the property of being functor algebras on +\begin_inset Formula $S$ \end_inset - is a monoid morphism between -\begin_inset Formula $M^{R}$ +. + In other words, both types +\begin_inset Formula $T$ \end_inset and -\begin_inset Formula $N^{R}$ +\begin_inset Formula $Z$ +\end_inset + + have operations induced by the recursion scheme +\begin_inset Formula $S$ \end_inset . - (The types -\begin_inset Formula $M^{R}$ + It is then natural to expect that a useful map between values of types + +\begin_inset Formula $T$ \end_inset and -\begin_inset Formula $N^{R}$ +\begin_inset Formula $Z$ \end_inset - are monoids due to Exercise -\begin_inset space ~ + should preserve the structure of those operations. + The mathematical formulation of that requirement is called the law of +\begin_inset Quotes eld \end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Exercise-monad-of-monoid-is-monoid" -plural "false" -caps "false" -noprefix "false" +\begin_inset Index idx +status open + +\begin_layout Plain Layout +functor algebra!morphism law +\end_layout \end_inset -). +functor algebra morphisms +\begin_inset Quotes erd +\end_inset + +. \end_layout \begin_layout Subsubsection -Exercise +Definition \begin_inset CommandInset label LatexCommand label -name "subsec:Exercise-traversables-10-1-1-1" +name "subsec:Definition-functor-algebra" \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:Exercise-traversables-10-1-1-1" +reference "subsec:Definition-functor-algebra" plural "false" caps "false" noprefix "false" @@ -27826,130 +28985,131 @@ noprefix "false" \end_layout \begin_layout Standard +Given a functor +\begin_inset Formula $S$ +\end_inset -\series bold -(a) -\series default - Each monad has at the same time an applicative functor instance. - Given a monad morphism -\begin_inset Formula $\phi:M\leadsto N$ + and two +\begin_inset Formula $S$ \end_inset - between two monads, show that -\begin_inset Formula $\phi$ +-functor algebras +\begin_inset Formula $X,$ \end_inset - is also an applicative morphism between applicative functors -\begin_inset Formula $M$ + +\begin_inset Formula $Y$ \end_inset - and -\begin_inset Formula $N$ +, with given structure maps +\begin_inset Formula $p_{X}:S^{X}\rightarrow X$ \end_inset -. -\end_layout + and +\begin_inset Formula $p_{Y}:S^{Y}\rightarrow Y$ +\end_inset -\begin_layout Standard +, a function +\begin_inset Formula $f:X\rightarrow Y$ +\end_inset + is called a \series bold -(b) +functor algebra morphism \series default - Show that the converse does not hold: if -\begin_inset Formula $\phi:M\leadsto N$ + +\begin_inset Index idx +status open + +\begin_layout Plain Layout +functor algebra morphism +\end_layout + \end_inset - is an applicative morphism between two monads then -\begin_inset Formula $\phi$ + if the following law holds: +\begin_inset Preview + +\begin_layout Standard +\begin_inset Formula +\[ +\xymatrix{\xyScaleY{1.4pc}\xyScaleX{3.0pc}S^{X}\ar[r]\sp(0.5){\ p_{X}}\ar[d]\sp(0.45){\,f^{\uparrow S}} & X\ar[d]\sp(0.45){\,f}\\ +S^{Y}\ar[r]\sp(0.5){~p_{Y}} & Y +} +\] + \end_inset - is -\emph on -not -\emph default - necessarily a monad morphism. -\end_layout -\begin_layout Subsubsection -Exercise -\begin_inset CommandInset label -LatexCommand label -name "subsec:Exercise-traversables-10-3" +\end_layout \end_inset -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Exercise-traversables-10-3" -plural "false" -caps "false" -noprefix "false" +\begin_inset Formula +\begin{equation} +p_{X}\bef f=f^{\uparrow S}\bef p_{Y}\quad.\label{eq:p-algebra-morphism-law-1} +\end{equation} + +\end_inset + +\begin_inset Formula $\square$ \end_inset \end_layout \begin_layout Standard -Given an applicative functor -\begin_inset Formula $M$ -\end_inset - - and a monoid morphism -\begin_inset Formula $\phi:R\rightarrow S$ +Suppose we are given a function +\begin_inset Formula $p_{Z}:S^{Z}\rightarrow Z$ \end_inset - between some monoid types -\begin_inset Formula $R$ +, which makes a type +\begin_inset Formula $Z$ \end_inset - and + into an \begin_inset Formula $S$ \end_inset -, prove that -\begin_inset Formula $\phi^{\uparrow M}:M^{R}\rightarrow M^{S}$ +-functor algebra. + Then it turns out that +\begin_inset Formula $\text{fold}_{S}(p_{Z}):T\rightarrow Z$ \end_inset - is also a monoid morphism. - (The types -\begin_inset Formula $M^{R}$ + is a functor algebra morphism between +\begin_inset Formula $T$ \end_inset and -\begin_inset Formula $M^{S}$ +\begin_inset Formula $Z$ \end_inset - are monoids due to Exercise -\begin_inset space ~ +. + In other words, the function +\begin_inset Formula $\text{fold}_{S}(p_{Z})$ \end_inset - -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Exercise-applicative-of-monoid-is-monoid" -plural "false" -caps "false" -noprefix "false" - + preserves the operations induced by the recursion scheme +\begin_inset Formula $S$ \end_inset -). +. \end_layout \begin_layout Subsubsection -Exercise +Statement \begin_inset CommandInset label LatexCommand label -name "subsec:Exercise-traversables-10-1-1" +name "subsec:Statement-catamorphism" \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:Exercise-traversables-10-1-1" +reference "subsec:Statement-catamorphism" plural "false" caps "false" noprefix "false" @@ -27960,386 +29120,408 @@ noprefix "false" \end_layout \begin_layout Standard -Given a monoid type -\begin_inset Formula $R$ +Consider a functor +\begin_inset Formula $S$ \end_inset - and an applicative morphism -\begin_inset Formula $\phi:M\leadsto N$ + and a recursive type +\begin_inset Formula $T$ \end_inset - between some applicative functors -\begin_inset Formula $M$ + defined by +\begin_inset Formula $T\triangleq S^{T}$ +\end_inset + +. + The functions +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +fix +\end_layout + +\end_inset + + +\begin_inset Formula $:S^{T}\rightarrow T$ \end_inset and -\begin_inset Formula $N$ +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +unfix +\end_layout + \end_inset -, prove that -\begin_inset Formula $\phi:M^{R}\rightarrow N^{R}$ + +\begin_inset Formula $:T\rightarrow S^{T}$ \end_inset - is a monoid morphism between -\begin_inset Formula $M^{R}$ + are assumed to be given. + For any type +\begin_inset Formula $Z$ \end_inset - and -\begin_inset Formula $N^{R}$ + and any function +\begin_inset Formula $g:S^{Z}\rightarrow Z$ \end_inset -. - (The types -\begin_inset Formula $M^{R}$ +, define +\begin_inset Formula $f:T\rightarrow Z$ \end_inset - and -\begin_inset Formula $N^{R}$ + by +\begin_inset Formula $f\triangleq\text{fold}_{S}(g)$ \end_inset - are monoids due to Exercise +, where +\begin_inset Formula $\text{fold}_{S}$ +\end_inset + + is defined in Section \begin_inset space ~ \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:Exercise-applicative-of-monoid-is-monoid" +reference "subsec:Recursion-schemes.-folding" plural "false" caps "false" noprefix "false" \end_inset -). -\end_layout +. + Then +\begin_inset Formula $f$ +\end_inset -\begin_layout Subsubsection -Exercise -\begin_inset CommandInset label -LatexCommand label -name "subsec:Exercise-traversables-10-3-1" + is a functor algebra morphism: it obeys Eq. +\begin_inset space ~ +\end_inset + +( +\begin_inset CommandInset ref +LatexCommand ref +reference "eq:p-algebra-morphism-law-1" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +) with +\begin_inset Formula $X=T$ +\end_inset + +, +\begin_inset Formula $p_{X}=\text{fix}$ +\end_inset + +, +\begin_inset Formula $Y=Z$ +\end_inset + +, +\begin_inset Formula $p_{Y}=g$ +\end_inset + +. + +\end_layout -\end_inset +\begin_layout Subparagraph +Proof +\end_layout +\begin_layout Standard +The functor algebra morphism law +\begin_inset space ~ +\end_inset +( \begin_inset CommandInset ref LatexCommand ref -reference "subsec:Exercise-traversables-10-3-1" +reference "eq:p-algebra-morphism-law-1" plural "false" caps "false" noprefix "false" \end_inset - -\end_layout - -\begin_layout Standard -Given a monoid -\begin_inset Formula $M$ +) of +\begin_inset Formula $f$ \end_inset -, define the functions -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -inMF -\end_layout + is: +\begin_inset Formula +\[ +\text{fix}\bef f\overset{?}{=}f^{\uparrow S}\bef g\quad. +\] \end_inset - and -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -outMF -\end_layout - +Write the code of +\begin_inset Formula $f\triangleq\text{fold}_{S}(g)$ \end_inset : \begin_inset Formula -\begin{align*} - & \text{inMF}:M\rightarrow M\rightarrow M\quad,\quad\quad\text{inMF}\,(x^{:M})\triangleq y^{:M}\rightarrow x\oplus y\quad,\\ - & \text{outMF}:(M\rightarrow M)\rightarrow M\quad,\quad\quad\text{outMF}\,(p^{:M\rightarrow M})\triangleq p(e_{M})\quad. -\end{align*} +\[ +f:T\rightarrow Z\quad,\quad\quad f\triangleq\text{unfix}\bef\overline{f}^{\uparrow S}\bef g\quad. +\] \end_inset -This definition of +Pre-compose \begin_inset listings inline true status open \begin_layout Plain Layout -inMF +fix \end_layout \end_inset - is similar to that used in the proof of Statement -\begin_inset space ~ + to both sides of the equation for +\begin_inset Formula $f$ \end_inset +: +\begin_inset Formula +\begin{align*} + & \text{fix}\bef f=\gunderline{\text{fix}\bef\text{unfix}}\bef\overline{f}^{\uparrow S}\bef g\\ +\text{recall that }\text{fix}\bef\text{unfix}=\text{id}:\quad & =\overline{f}^{\uparrow S}\bef g\quad. +\end{align*} -\begin_inset CommandInset ref -LatexCommand ref -reference "subsec:Statement-foldleft-foldmap-equivalence" -plural "false" -caps "false" -noprefix "false" +\end_inset +We have obtained the required law. + +\begin_inset Formula $\square$ \end_inset -(c). + \end_layout \begin_layout Standard - -\series bold -(a) -\series default - Prove that +A similar property holds for the function \begin_inset listings inline true status open \begin_layout Plain Layout -inMF +unfold \end_layout \end_inset - is a monoid morphism between -\begin_inset Formula $M$ +. + For any function +\begin_inset Formula $g:Z\rightarrow S^{Z}$ \end_inset - and the monoid -\begin_inset Formula $\text{MF}^{M}$ + we define +\begin_inset Formula $f:Z\rightarrow T$ \end_inset - consisting of all functions of type -\begin_inset Formula $M\rightarrow M$ + by +\begin_inset Formula $f\triangleq\text{unfold}_{S}(g)$ \end_inset . - Define the empty element and the binary operation of -\begin_inset Formula $\text{MF}^{M}$ -\end_inset - - appropriately. -\end_layout - -\begin_layout Standard - -\series bold -(b) -\series default - Prove that -\begin_inset Formula $\text{inMF}\bef\text{outMF}=\text{id}$ -\end_inset + Then we will have: +\begin_inset Formula +\[ +f\bef\text{unfix}=g\bef f^{\uparrow S}\quad. +\] - but -\begin_inset Formula $\text{outMF}\bef\text{inMF}\neq\text{id}$ \end_inset -. - (Give an example of a monoid -\begin_inset Formula $M$ +This property is called the +\begin_inset Quotes eld \end_inset - where the second equation does not hold.) -\end_layout -\begin_layout Standard +\begin_inset Formula $S$ +\end_inset -\series bold -(c) -\series default - Prove that -\begin_inset listings -inline true +- +\begin_inset Index idx status open \begin_layout Plain Layout - -outMF +functor coalgebra morphism law \end_layout \end_inset - is -\emph on -not -\emph default - a monoid morphism between -\begin_inset Formula $\text{MF}^{M}$ + +\series bold +functor coalgebra morphism +\series default + law +\begin_inset Quotes erd \end_inset - and -\begin_inset Formula $M$ + in category theory. + Intuitively, it means that the function +\begin_inset Formula $f$ +\end_inset + +, in a certain precise sense, preserves the operations required by the recursion + scheme +\begin_inset Formula $S$ \end_inset . + \end_layout -\begin_layout Subsubsection -Exercise -\begin_inset CommandInset label -LatexCommand label -name "subsec:Exercise-traversables-10-1-1-2" - +\begin_layout Standard +The proof is analogous to that of Statement +\begin_inset space ~ \end_inset \begin_inset CommandInset ref LatexCommand ref -reference "subsec:Exercise-traversables-10-1-1-2" +reference "subsec:Statement-catamorphism" plural "false" caps "false" noprefix "false" \end_inset - -\end_layout - -\begin_layout Standard -For any applicative functor -\begin_inset Formula $F$ -\end_inset - - with a known -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -zip -\end_layout - +. + Write the code of +\begin_inset Formula $f\triangleq\text{unfold}_{S}(g)$ \end_inset - method, define the -\begin_inset Quotes eld -\end_inset +: +\begin_inset Formula +\[ +f\triangleq g\bef f^{\uparrow S}\bef\text{fix}\quad. +\] -reversed -\begin_inset Quotes erd \end_inset - +It remains to compose \begin_inset listings inline true status open \begin_layout Plain Layout -zip +unfix \end_layout \end_inset - method as: + with both sides of the equation above: \begin_inset Formula \[ -\text{zip}_{\text{rev}F}:F^{A}\times F^{B}\rightarrow F^{A\times B}\quad,\quad\quad\text{zip}_{\text{rev}F}\triangleq\text{swap}\bef\text{zip}_{F}\bef\text{swap}^{\uparrow F}\quad. +f\bef\text{unfix}=g\bef f^{\uparrow S}\bef\gunderline{\text{fix}\bef\text{unfix}}=g\bef f^{\uparrow S}\quad. \] \end_inset +This proves the functor coalgebra morphism law for +\begin_inset Formula $f$ +\end_inset +. \end_layout \begin_layout Standard - -\series bold -(a) -\series default - Show that -\begin_inset Formula $\text{zip}_{\text{rev}F}$ -\end_inset - - also provides a lawful applicative instance for -\begin_inset Formula $F$ +Note that these properties of +\begin_inset Formula $\text{fold}_{S}(g)$ \end_inset - (where -\begin_inset Formula $F$ + and +\begin_inset Formula $\text{unfold}_{S}(g)$ \end_inset -'s + hold only under the assumption that the resulting functions terminate. + The proofs do not show that those functions must terminate. + As we have seen, functions obtained via \begin_inset listings inline true status open \begin_layout Plain Layout -pure +fold \end_layout \end_inset - method remains unchanged). - Show that -\begin_inset Formula $\text{zip}_{\text{rev}F}=\text{zip}_{F}$ -\end_inset + and +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +unfold +\end_layout - whenever -\begin_inset Formula $F$ \end_inset - is commutative. + will in some cases loop forever. + The laws only hold when the functions do not generate infinite loops. \end_layout \begin_layout Standard +In the mathematical literature, the function +\begin_inset Formula $\text{fold}_{S}$ +\end_inset + is called a \series bold -(b) +catamorphism \series default - Show that the -\begin_inset Quotes eld -\end_inset -applicative reversal -\begin_inset Quotes erd -\end_inset +\begin_inset Index idx +status open - obeys an applicative naturality law: for any applicative functors -\begin_inset Formula $F$ -\end_inset +\begin_layout Plain Layout +catamorphism +\end_layout -, -\begin_inset Formula $G$ \end_inset - and an applicative morphism -\begin_inset Formula $\phi:F^{A}\rightarrow G^{A}$ +, and +\begin_inset Formula $\text{unfold}_{S}$ \end_inset -, the following equation holds: -\begin_inset Formula -\[ -(p^{:F^{A}}\times q^{:F^{B}})\triangleright\text{zip}_{\text{rev}F}\triangleright\phi=(\phi(p)\times\phi(q))\triangleright\text{zip}_{\text{rev}G}\quad. -\] + is called an +\begin_inset Index idx +status open -\end_inset +\begin_layout Plain Layout +anamorphism +\end_layout +\end_inset -\end_layout -\begin_layout Section -Discussion and further developments +\series bold +anamorphism +\series default +. \end_layout \begin_layout Subsection @@ -31487,8 +32669,8 @@ In most of this book, recursive type constructors are defined via equations \end_inset is a recursion scheme. - We have seen one example of a recursive type constructor (a perfect-shaped - tree, Sections + We have seen one example of a recursive type constructor that cannot be + defined in this way: a perfect-shaped tree (see Section \begin_inset space ~ \end_inset @@ -31502,7 +32684,7 @@ noprefix "false" \end_inset - and + and Example \begin_inset space ~ \end_inset @@ -31516,9 +32698,8 @@ noprefix "false" \end_inset -) that cannot be defined in this way. - The reason is that the recursive type equation for a perfect-shaped binary - tree +). + The recursive type equation for a perfect-shaped binary tree \begin_inset Index idx status open @@ -31528,11 +32709,11 @@ perfect-shaped tree \end_inset - + ( \begin_inset Formula $\text{PT}$ \end_inset - is: +) is: \begin_inset Formula \begin{equation} \text{PT}^{A}\triangleq A+\text{PT}^{A\times A}\quad.\label{eq:perfect-shaped-binary-tree-type-equation} @@ -31571,7 +32752,7 @@ noprefix "false" \end_inset -) via a recursion scheme, we introduce an additional functor +) via a recursion scheme, we need to introduce an additional functor \begin_inset Formula $P$ \end_inset diff --git a/sofp-src/lyx/sofp-typeclasses.lyx b/sofp-src/lyx/sofp-typeclasses.lyx index 5455ef7d8..467b49eb1 100644 --- a/sofp-src/lyx/sofp-typeclasses.lyx +++ b/sofp-src/lyx/sofp-typeclasses.lyx @@ -1865,7 +1865,7 @@ p \end_inset - can be converted into a total function by changing its type to + can be made into a total function by changing its type to \begin_inset listings inline true status open @@ -1879,7 +1879,10 @@ Left[Int, String] => Int \end_inset . - Another example: applying +\end_layout + +\begin_layout Standard +Another example: applying \begin_inset listings inline true status open @@ -1915,7 +1918,7 @@ NonEmptyList \end_inset - guarantees at compile time that the first element exists: + guarantees that the first element exists: \begin_inset listings inline false status open @@ -1932,6 +1935,10 @@ val h = xs.head // _.head is a total function for a NonEmptyList. \end_inset + +\end_layout + +\begin_layout Standard In these cases, we achieve safety by making types more constrained. Similarly, partial type-to-value functions (PTVFs) become safe to use if we impose suitable typeclass constraints on the type parameters. @@ -2524,75 +2531,82 @@ def avg[T](s: Seq[T], frac: Frac[T]): T \end_inset -The value +We will be unable to use \begin_inset listings inline true status open \begin_layout Plain Layout -frac: Frac[T] +avg[T] \end_layout \end_inset - is called a -\begin_inset Index idx + with types +\begin_inset listings +inline true status open \begin_layout Plain Layout -typeclass!instance value + +T \end_layout \end_inset - -\series bold -typeclass instance -\series default - value. - Because that value needs to be passed to every call of + for which we have no values of type \begin_inset listings inline true status open \begin_layout Plain Layout -avg[T] +Frac[T] \end_layout \end_inset -, we will be unable to use types + because such a value needs to be passed to every call to \begin_inset listings inline true status open \begin_layout Plain Layout -T +avg[T] \end_layout \end_inset - for which -\begin_inset listings -inline true +. + Such values are called +\begin_inset Quotes eld +\end_inset + +typeclass instance values +\begin_inset Quotes erd +\end_inset + + or, for brevity, just +\series bold +typeclass instances +\series default +. +\begin_inset Index idx status open \begin_layout Plain Layout - -Frac[T] +typeclass!instance value \end_layout \end_inset - is void (i.e., has no values). - + \end_layout \begin_layout Standard -A typeclass instance argument such as +A typeclass instance argument like \begin_inset listings inline true status open @@ -2604,7 +2618,7 @@ frac: Frac[T] \end_inset - is called an + is also called an \series bold evidence parameter \series default @@ -2649,23 +2663,7 @@ T \end_inset belongs to the type domain of the typeclass. - Such evidence values are called -\begin_inset Quotes eld -\end_inset - -typeclass instance values -\begin_inset Quotes erd -\end_inset - - or, for brevity, just -\begin_inset Quotes eld -\end_inset - -typeclass instances -\begin_inset Quotes erd -\end_inset - -. + \end_layout \begin_layout Standard @@ -3102,7 +3100,7 @@ def avg[T](s: Seq[T], frac: Frac[T]): T = { // Assuming `s` is non-empty. \begin_layout Plain Layout - frac.intdiv(sum, s.length) // Compute `sum/length`. + frac.intdiv(sum, s.length) // Compute `sum/s.length`. \end_layout \begin_layout Plain Layout @@ -3386,7 +3384,7 @@ trait \end_inset --based code is significantly longer than the code based on a case class. +-based code is longer than the code based on a case class. One advantage of the longer code is the ability to combine typeclasses by inheriting from \begin_inset listings @@ -3402,8 +3400,8 @@ trait s. We will look at that in more detail below. - For now, we note that both implementations will require the programmer - to add a significant amount of new code: + For now, we note that both implementations require the programmer to add + a significant amount of new code: \end_layout \begin_layout Itemize @@ -3503,8 +3501,8 @@ implicit value \end_inset - declaration is a feature of Scala that makes arguments automatically available - to functions that declare + declaration is a feature of Scala that passes arguments automatically to + functions that declare \begin_inset Quotes eld \end_inset @@ -3541,8 +3539,8 @@ Int \end_inset into the current scope. - That value will be automatically passed as an argument to any function - declaring an argument of type + That value will be automatically passed to any function declaring an argument + of type \begin_inset listings inline true status open @@ -3608,7 +3606,7 @@ res0: String = xyz with 123 \end_inset -We need to declare the arguments as +We need to declare the implicit arguments using the Scala keyword \begin_inset listings inline true status open @@ -3620,8 +3618,28 @@ implicit \end_inset - in the function's type signature, and the implicit arguments must be in - a + (in Scala 3, the keyword +\begin_inset Quotes eld +\end_inset + + +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +given +\end_layout + +\end_inset + + +\begin_inset Quotes erd +\end_inset + +). + All the implicit arguments must be in a \emph on separate \emph default @@ -3635,7 +3653,7 @@ argument list \end_inset -argument list. +argument list in the function's type signature. \end_layout \begin_layout Standard @@ -3652,6 +3670,18 @@ implicitly \end_inset +; in Scala 3, it is called +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +summon +\end_layout + +\end_inset + . Compare its code with the code of the ordinary identity function: \begin_inset listings @@ -4112,7 +4142,7 @@ def avg[T](s: Seq[T])(implicit frac: Frac[T]): T = { \begin_layout Plain Layout - frac.intdiv(sum, s.length) // Compute `sum/length`. + frac.intdiv(sum, s.length) \end_layout \begin_layout Plain Layout @@ -4614,7 +4644,7 @@ xs.map(f).filter(g) \end_inset -, because that code is easier to read than +, because that code is easier to work with than \begin_inset listings inline true status open @@ -4987,7 +5017,7 @@ AvgSyntax \end_inset . - These values will be created automatically because the class constructor + That value will be created automatically because the class constructor of \begin_inset listings inline true @@ -5085,7 +5115,7 @@ This example illustrates the convenience of implementing PTVFs as extension \emph on only \emph default - on values of supported types. + for supported types. \end_layout \begin_layout Standard @@ -7162,7 +7192,7 @@ status open \end_inset . - This makes programs written using the semigroup operation + This makes code involving the semigroup operation ( \begin_inset listings inline true status open @@ -7174,7 +7204,7 @@ status open \end_inset - easier to understand and reason about. +) easier to read and to reason about. \end_layout \begin_layout Standard @@ -7546,7 +7576,7 @@ concat \end_inset - function, requires significant work (see Section + function, requires some work (see Section \begin_inset space ~ \end_inset @@ -7800,7 +7830,7 @@ T \end_inset -The implementation is possible for any types +The implementation works for any types \begin_inset Formula $A$ \end_inset @@ -7825,9 +7855,9 @@ implicit def semigroup2[A, B] = Semigroup[(A, B)]{ case ((a1, b1), (a2, \end_layout \begin_layout Standard -One use case for this semigroup is to maintain a pair of timestamps for - the first and the last events in a temporally ordered series. - Merging two such pairs for consecutive events means to keep the first value +One use case for this semigroup is for maintaining a pair of timestamps + for the first and the last events in a temporally ordered series. + Merging two such pairs for consecutive events will keep the first value from the first pair and the second value from the second pair. \end_layout @@ -7994,7 +8024,7 @@ empty \begin_inset Quotes erd \end_inset - value for lists. + value for the list concatenation. Adding zero to an integer does not change that integer; so, zero is the \begin_inset Quotes eld @@ -8004,7 +8034,7 @@ empty \begin_inset Quotes erd \end_inset - value when adding integers. + value for the addition. \end_layout \begin_layout Standard @@ -8678,7 +8708,7 @@ HasDefault \end_inset - instance is not guaranteed to satisfy the identity laws + instance will not necessarily obey the identity laws \begin_inset space ~ \end_inset @@ -8792,11 +8822,8 @@ Monoid \end_inset instance must be defined. -\end_layout - -\begin_layout Standard \begin_inset Note Note -status open +status collapsed \begin_layout Plain Layout Are there alternative implementations of the @@ -9078,19 +9105,11 @@ Functor \end_inset typeclass must contain this function as a value. - But defining the typeclass as before via a -\begin_inset listings -inline true -status open - -\begin_layout Plain Layout - -case class -\end_layout - -\end_inset - - does not work with Scala 2: + Scala 3 directly supports an argument type that +\emph on +itself +\emph default + contains type quantifiers: \begin_inset listings lstparams "mathescape=true" inline false @@ -9098,25 +9117,12 @@ status open \begin_layout Plain Layout -final case class Functor[F[_]](map: $ -\backslash -forall(A,B). -\backslash -,$F[A] => (A => B) => F[B]) // Not possible in Scala 2. +final case class Functor[F[_]](map: [A] => [B] => F[A] => (A => B) => F[B]) \end_layout \end_inset -Scala 3 directly supports an argument type that -\emph on -itself -\emph default - contains type quantifiers such as -\begin_inset Formula $\forall(A,B)$ -\end_inset - -. - In Scala 2, we have to replace nested type quantifiers by a +In Scala 2, we have to use a \begin_inset listings inline true status open @@ -9140,7 +9146,7 @@ def \end_inset - method: + method like this: \begin_inset Index idx status open @@ -9200,21 +9206,20 @@ F[_] \end_inset -, which is a type constructor. - For any type constructor +, indicating that \begin_inset listings inline true status open \begin_layout Plain Layout -\noindent F \end_layout \end_inset -, a value of type + is itself a type constructor. + A value of type \begin_inset listings inline true status open @@ -9239,7 +9244,7 @@ status open \begin_layout Plain Layout -Functor +Functor[F] \end_layout \end_inset @@ -9287,7 +9292,7 @@ implicit val functorSeq = new Functor[Seq] { \end_inset -This is currently the most common way of defining typeclasses in Scala. +This is currently the most common way of defining such typeclasses in Scala. \end_layout \begin_layout Standard @@ -9315,293 +9320,994 @@ Functor \end_inset - type constructors: + type constructors: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +implicit class FunctorSyntax[F[_]: Functor, A](fa: F[A]) { // Syntax + helper. +\end_layout + +\begin_layout Plain Layout + + def map[B](f: A => B): F[B] = implicitly[Functor[F]].map(fa)(f) +\end_layout + +\begin_layout Plain Layout + +} +\end_layout + +\end_inset + +If this class definition is in scope, the +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +map +\end_layout + +\end_inset + + method becomes available for values of functor-wrapped types. +\end_layout + +\begin_layout Standard +Using the +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Functor +\end_layout + +\end_inset + + typeclass and the syntax helper, we can now implement the function +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +inject +\end_layout + +\end_inset + +: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +def inject[F[_]: Functor, A, B](a: A, f: F[B]): F[(A, B)] = +\end_layout + +\begin_layout Plain Layout + + f.map { b => (a, b) } +\end_layout + +\begin_layout Plain Layout + +\end_layout + +\begin_layout Plain Layout + +scala> inject( +\begin_inset Quotes eld +\end_inset + +abc +\begin_inset Quotes erd +\end_inset + +, Seq(1, 2, 3)) // Need an implicit Functor[Seq] here. +\end_layout + +\begin_layout Plain Layout + +res0: Seq[(String, Int)] = List(("abc", 1), ("abc", 2), ("abc", 3)) +\end_layout + +\end_inset + + Similarly to the +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Monoid +\end_layout + +\end_inset + + typeclass, the code of the +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Functor +\end_layout + +\end_inset + + typeclass does not enforce the functor laws on the implementation. + It is the programmer's responsibility to verify that the laws hold. +\end_layout + +\begin_layout Standard +One way of checking the laws is to use the +\family typewriter +scalacheck +\family default + library +\begin_inset Index idx +status open + +\begin_layout Plain Layout + +\family typewriter +scalacheck +\family default + library +\end_layout + +\end_inset + + +\begin_inset Index idx +status open + +\begin_layout Plain Layout +verifying laws with +\family typewriter +scalacheck +\end_layout + +\end_inset + + +\begin_inset Foot +status open + +\begin_layout Plain Layout + +\family typewriter +\begin_inset CommandInset href +LatexCommand href +target "https://www.scalacheck.org" +literal "false" + +\end_inset + + +\end_layout + +\end_inset + + that automatically runs random tests for the given properties, trying to + discover a set of values for which some property fails to hold. + Using the +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Functor +\end_layout + +\end_inset + + typeclass constraint, we can implement a function (in our terminology, + a PTVF) that checks the functor laws for +\emph on +any +\emph default + given type constructor +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +F +\end_layout + +\end_inset + +: +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +import org.scalacheck.Arbitrary // Necessary imports and definitions. +\end_layout + +\begin_layout Plain Layout + +import org.scalatest.prop.GeneratorDrivenPropertyChecks +\end_layout + +\begin_layout Plain Layout + +class FunctorTest extends Matchers with GeneratorDrivenPropertyChecks { +\end_layout + +\begin_layout Plain Layout + + def checkFunctorLaws[F[_], A, B, C]()(implicit ff: Functor[F], +\end_layout + +\begin_layout Plain Layout + + fa: Arbitrary[F[A]], ab: Arbitrary[A => B], bc: Arbitrary[B => C]) + = { +\end_layout + +\begin_layout Plain Layout + + forAll { (fa: F[A]) => fa.map(identity[A]) shouldEqual fa } // Identity + law. + +\begin_inset Quotes eld +\end_inset + +For all `fa`, ... +\begin_inset Quotes erd +\end_inset + + +\end_layout + +\begin_layout Plain Layout + + forAll { (f: A => B, g: B => C, fa: F[A]) => // Composition + law. + The assertion must hold for all `f`, `g`, `fa`. +\end_layout + +\begin_layout Plain Layout + + fa.map(f).map(g) shouldEqual fa.map(f andThen g) +\end_layout + +\begin_layout Plain Layout + + } +\end_layout + +\begin_layout Plain Layout + + } +\end_layout + +\begin_layout Plain Layout + + // Check the laws for F = Seq, A = Int, B = String, C = Double. +\end_layout + +\begin_layout Plain Layout + + checkFunctorLaws[Seq, Int, String, Double]() +\end_layout + +\begin_layout Plain Layout + +} +\end_layout + +\end_inset + + +\end_layout + +\begin_layout Standard +The +\family typewriter +scalacheck +\family default + library will substitute a large number of random values into the given + assertions. + But the laws will be tested only with a finite number of values and with + type parameters set to specific types. + While it is useful to test laws with +\family typewriter +scalacheck +\family default + (we might find a bug), only a symbolic derivation provides a rigorous proof + that the laws hold. + One of the main themes of this book is to show how to perform such symbolic + derivations. +\end_layout + +\begin_layout Subsection +Non-extension methods of typeclasses +\end_layout + +\begin_layout Standard +Most typeclasses contain methods that consume the values of a type that + is constrained to belong to that typeclass. + As shown in Example +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:tc-Example-metadata-extractors" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +, if a type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +T +\end_layout + +\end_inset + + belongs to the typeclass +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +HasMetadata +\end_layout + +\end_inset + + then the method +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +getName: T => String +\end_layout + +\end_inset + + will consume a value of type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +T +\end_layout + +\end_inset + +. + In those cases, it is convenient to define typeclass methods as extension + methods and write code as +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +t.getName +\end_layout + +\end_inset + + for values +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +t: T +\end_layout + +\end_inset + +. +\end_layout + +\begin_layout Standard +In some cases, however, a typeclass will contain methods that do +\emph on +not +\emph default + consume values of the types +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +T +\end_layout + +\end_inset + + that belong to the typeclass. + We will call them +\begin_inset Quotes eld +\end_inset + +non-extension methods +\begin_inset Index idx +status open + +\begin_layout Plain Layout +\begin_inset Quotes eld +\end_inset + +non-extension methods +\end_layout + +\end_inset + + +\begin_inset Quotes erd +\end_inset + +. +\end_layout + +\begin_layout Standard +Consider Example +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:tc-Example-Pointed-type" +plural "false" +caps "false" +noprefix "false" + +\end_inset + + showing the typeclass +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +HasDefault +\end_layout + +\end_inset + + with the method +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +value +\end_layout + +\end_inset + + that +\emph on +returns +\emph default + values of type +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +T +\end_layout + +\end_inset + +. + To access the functionality of that typeclass, we need to write code such + as +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +implicitly[HasDefault[T]].value +\end_layout + +\end_inset + +. + The method +\begin_inset Quotes eld +\end_inset + + +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +value +\end_layout + +\end_inset + + +\begin_inset Quotes erd +\end_inset + + is not defined as an extension method on +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +T +\end_layout + +\end_inset + +. +\end_layout + +\begin_layout Standard +Another such case is the +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +empty +\end_layout + +\end_inset + + method of the +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Monoid +\end_layout + +\end_inset + + typeclass shown in Example +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:tc-Example-Monoids" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +. + In programs that use the +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +Monoid +\end_layout + +\end_inset + + typeclass, we could write +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +implicitly[Monoid[T]].empty +\end_layout + +\end_inset + + when we need to access that method. +\end_layout + +\begin_layout Standard +What if we define +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +empty +\end_layout + +\end_inset + + as an extension method on a value +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +t: T +\end_layout + +\end_inset + +? +\begin_inset listings +inline false +status open + +\begin_layout Plain Layout + +implicit class MonoidEmpty[T: Monoid](t: T) { +\end_layout + +\begin_layout Plain Layout + + def empty: T = implicitly[Monoid[T]].empty +\end_layout + +\begin_layout Plain Layout + +} +\end_layout + +\end_inset + +This code works but is not helpful. + We will be able to write +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout + +t.empty +\end_layout + +\end_inset + + only if we already have a value \begin_inset listings -inline false +inline true status open \begin_layout Plain Layout -implicit class FunctorSyntax[F[_]: Functor, A](fa: F[A]) { // Syntax - helper. +t \end_layout -\begin_layout Plain Layout +\end_inset - def map[B](f: A => B): F[B] = implicitly[Functor[F]].map(fa)(f) -\end_layout + of a monoidal type +\begin_inset listings +inline true +status open \begin_layout Plain Layout -} +T \end_layout \end_inset -If this class definition is in scope, the +. + However, the importance of a monoid's \begin_inset listings inline true status open \begin_layout Plain Layout -map +empty \end_layout \end_inset - method becomes available for values of functor types. -\end_layout - -\begin_layout Standard -Using the + method is that it can provide a value of the monoidal type \begin_inset listings inline true status open \begin_layout Plain Layout -Functor +T \end_layout \end_inset - typeclass and the syntax helper, we can now implement the function + in a program scope where no other values of type \begin_inset listings inline true status open \begin_layout Plain Layout -inject +T \end_layout \end_inset -: + are available. + Consider this code: \begin_inset listings +lstparams "numbers=left" inline false status open \begin_layout Plain Layout -def inject[F[_]: Functor, A, B](a: A, f: F[B]): F[(A, B)] = +def toMonoid[T: Monoid]: Option[T] => T = { \end_layout \begin_layout Plain Layout - f.map { b => (a, b) } + case None => implicitly[Monoid[T]].empty \end_layout \begin_layout Plain Layout + case Some(t) => t \end_layout \begin_layout Plain Layout -scala> inject( -\begin_inset Quotes eld -\end_inset +} +\end_layout -abc -\begin_inset Quotes erd \end_inset -, Seq(1, 2, 3)) // Need an implicit Functor[Seq] here. -\end_layout +In the scope of line 2, the code has no given values of type +\begin_inset listings +inline true +status open \begin_layout Plain Layout -res0: Seq[(String, Int)] = List(("abc", 1), ("abc", 2), ("abc", 3)) +T \end_layout \end_inset - Similarly to the +. + The \begin_inset listings inline true status open \begin_layout Plain Layout -Monoid +empty \end_layout \end_inset - typeclass, the code of the + method is the only way of producing such values. +\end_layout + +\begin_layout Standard +To use non-extension methods of typeclasses more easily, Scala libraries + often implement helper methods in a companion object of the typeclass. + We will now show two ways of doing that for the \begin_inset listings inline true status open \begin_layout Plain Layout -Functor +Monoid \end_layout \end_inset - typeclass does not enforce the functor laws on the implementation. - It is the programmer's responsibility to verify that the laws hold. + typeclass. + \end_layout \begin_layout Standard -One way of checking the laws is to use the -\family typewriter -scalacheck -\family default - library -\begin_inset Index idx +The first option is to define +\begin_inset listings +inline true status open \begin_layout Plain Layout -\family typewriter -scalacheck -\family default - library +empty[T] \end_layout \end_inset - -\begin_inset Index idx + as a method in the companion object: +\begin_inset listings +inline false status open \begin_layout Plain Layout -verifying laws with -\family typewriter -scalacheck -\end_layout -\end_inset +object Monoid { +\end_layout +\begin_layout Plain Layout -\begin_inset Foot -status open + def empty[T: Monoid]: T = implicitly[Monoid[T]].empty +\end_layout \begin_layout Plain Layout -\family typewriter -\begin_inset CommandInset href -LatexCommand href -target "https://www.scalacheck.org" -literal "false" +} +\end_layout \end_inset +Then we may write +\begin_inset listings +inline true +status open + +\begin_layout Plain Layout +Monoid.empty[T] \end_layout \end_inset - that automatically runs random tests for the given assertions, trying to - discover a set of values for which some assertion fails. - Using the + instead of \begin_inset listings inline true status open \begin_layout Plain Layout -Functor +implicitly[Monoid[T]].empty \end_layout \end_inset - typeclass constraint, we can implement a function (in our terminology, - a PTVF) that checks the functor laws for -\emph on -any -\emph default - given type constructor +. +\end_layout + +\begin_layout Standard +A disadvantage of this approach is that the companion object will need to + implement one helper method for each of the non-extension methods of a + typeclass. + This is not an issue for \begin_inset listings inline true status open \begin_layout Plain Layout -F +Monoid \end_layout \end_inset -: + since there is only one non-extension method. +\end_layout + +\begin_layout Standard +The second option is to define a helper method that fetches the typeclass + evidence value from the implicit scope: \begin_inset listings inline false status open \begin_layout Plain Layout -import org.scalacheck.Arbitrary // Necessary imports and definitions. +object Monoid { \end_layout \begin_layout Plain Layout -import org.scalatest.prop.GeneratorDrivenPropertyChecks + def apply[T: Monoid]: Monoid[T] = implicitly[Monoid[T]] \end_layout \begin_layout Plain Layout -class FunctorTest extends Matchers with GeneratorDrivenPropertyChecks { +} \end_layout -\begin_layout Plain Layout +\end_inset - def checkFunctorLaws[F[_], A, B, C]()(implicit ff: Functor[F], -\end_layout +Then we may write +\begin_inset listings +inline true +status open \begin_layout Plain Layout - fa: Arbitrary[F[A]], ab: Arbitrary[A => B], bc: Arbitrary[B => C]) - = { +Monoid[T].empty \end_layout -\begin_layout Plain Layout - - forAll { (fa: F[A]) => fa.map(identity[A]) shouldEqual fa } // Identity - law. - -\begin_inset Quotes eld \end_inset -For all `fa`, ... -\begin_inset Quotes erd -\end_inset + instead of +\begin_inset listings +inline true +status open +\begin_layout Plain Layout +implicitly[Monoid[T]].empty \end_layout -\begin_layout Plain Layout +\end_inset - forAll { (f: A => B, g: B => C, fa: F[A]) => // Composition - law. - The assertion must hold for all `f`, `g`, `fa`. -\end_layout +. + This approach works for typeclasses with any number of non-extension methods. + As an example, let us re-implement the function +\begin_inset listings +inline true +status open \begin_layout Plain Layout - fa.map(f).map(g) shouldEqual fa.map(f andThen g) +toMonoid \end_layout -\begin_layout Plain Layout +\end_inset - } -\end_layout + using this technique: +\begin_inset listings +inline false +status open \begin_layout Plain Layout - } +def toMonoid[T: Monoid]: Option[T] => T = { \end_layout \begin_layout Plain Layout - // Check the laws for F = Seq, A = Int, B = String, C = Double. + case None => Monoid[T].empty \end_layout \begin_layout Plain Layout - checkFunctorLaws[Seq, Int, String, Double]() + case Some(t) => t \end_layout \begin_layout Plain Layout @@ -9611,26 +10317,13 @@ For all `fa`, ... \end_inset - +This is the style preferred by many Scala libraries. + \end_layout \begin_layout Standard -The -\family typewriter -scalacheck -\family default - library will substitute a large number of random values into the given - assertions. - But the laws will be tested only with a finite number of values and with - type parameters set to specific types. - While it is useful to test laws with -\family typewriter -scalacheck -\family default - (we might find a bug), only a symbolic derivation provides a rigorous proof - that the laws hold. - One of the main themes of this book is to show how to perform such symbolic - derivations. +The code examples in this book will assume that typeclasses define non-extension + methods in their companion objects in some way. \end_layout \begin_layout Section @@ -9652,8 +10345,9 @@ noprefix "false" \end_inset - we analyzed the structure of functors by checking which of the six standard - type constructions can make new functors out of previous ones. + we analyzed the structure of functors and contrafunctors by checking which + of the six standard type constructions can make new functors or contrafunctors + out of previous ones. We will now apply the same \begin_inset Index idx status open @@ -10096,7 +10790,7 @@ Extractor \end_inset instance (since there are no laws to check). - However, every choice will use one of the two + However, every choice will use only one of the two \begin_inset listings inline true status open @@ -10275,22 +10969,22 @@ def extractorEither[Z, A, B](implicit ti1: Extractor[Z, A], ti2: Extractor[Z, \begin_layout Plain Layout - Extractor[Z, Either[A, B]] { + Extractor[Z, Either[A, B]] { \end_layout \begin_layout Plain Layout - case Left(a) => ti1.extract(a) + case Left(a) => ti1.extract(a) \end_layout \begin_layout Plain Layout - case Right(b) => ti2.extract(b) + case Right(b) => ti2.extract(b) \end_layout \begin_layout Plain Layout -} + } \end_layout \end_inset @@ -10303,7 +10997,7 @@ So, the co-product of \begin_inset Formula $B$ \end_inset - can be given a unique + has a unique \begin_inset listings inline true status open @@ -10884,19 +11578,15 @@ Extractor \begin_inset Formula $Z$ \end_inset - or other fixed types.) For each of -\begin_inset Formula $F_{1}$ -\end_inset - -, -\begin_inset Formula $F_{2}$ + or other fixed types.) For each +\begin_inset Formula $F=F_{i}$ \end_inset -, and/or -\begin_inset Formula $F_{3}$ + (with +\begin_inset Formula $i=1,2,3$ \end_inset -, we implemented a function of type: +), we implemented a function of type: \begin_inset Formula \[ \text{extractorF}:\text{Extractor}^{A}\rightarrow\text{Extractor}^{F^{A}}\quad. @@ -10973,12 +11663,12 @@ The types match because the type \end_inset . - As long as the definition of the recursive type + As long as the recursive type \begin_inset Formula $T$ \end_inset - is valid (i.e., the type recursion terminates), the extractor function will - also terminate. + is not void (i.e., the type recursion terminates), the extractor function + will also terminate. \end_layout \begin_layout Standard @@ -11127,32 +11817,36 @@ status open \begin_layout Plain Layout def extractorS[A](f: Extractor[A]): Extractor[S[A]] = Extractor[S[A]] { - case (c, g) => \end_layout \begin_layout Plain Layout - g(c) match { + case (c, g) => \end_layout \begin_layout Plain Layout - case Left(z) => z + g(c) match { \end_layout \begin_layout Plain Layout - case Right((_, t)) => f.extract(t) + case Left(z) => z \end_layout \begin_layout Plain Layout - } + case Right((_, t)) => f.extract(t) \end_layout \begin_layout Plain Layout -} + } +\end_layout + +\begin_layout Plain Layout + + } \end_layout \end_inset @@ -11177,7 +11871,7 @@ Extractor \begin_inset Formula $T$ \end_inset - by a recursive equation: + by: \begin_inset Formula \begin{equation} \text{extractorT}\triangleq\text{extractorS}\left(\text{extractorT}\right)\quad.\label{eq:recursive-extractor-def} @@ -11289,7 +11983,7 @@ Why the recursion terminates \end_layout \begin_layout Standard -The above code shows that the recursive definition +The test code above shows that the recursive definition \begin_inset space ~ \end_inset @@ -11304,7 +11998,11 @@ noprefix "false" \end_inset ) terminates. - Why does it? A recursive definition of the form + Why does it, though? A recursive +\emph on +value +\emph default + definition of the form \begin_inset Formula $x\triangleq f(x)$ \end_inset @@ -11426,7 +12124,7 @@ Although a function \begin_inset Formula $f$ \end_inset - is equivalent to its + and its \series bold expanded form \series default @@ -11444,8 +12142,8 @@ expanded form of a function!use in recursive values \begin_inset Formula $t\rightarrow f(t)$ \end_inset -, there is an important difference: using expanded forms in the code will - make recursive definitions + are equivalent, there is an important difference: writing expanded forms + of functions will make recursive value definitions \begin_inset Index idx status open @@ -11475,12 +12173,12 @@ status open \begin_layout Plain Layout -val f = k(f) +def f = k(f) \end_layout \end_inset - creates an infinite loop when we compute anything that involves + creates an infinite loop whenever we compute anything that involves \begin_inset Formula $f$ \end_inset @@ -11531,11 +12229,15 @@ java.lang.StackOverflowError: ... \end_inset -This code is clearly invalid. - But if we expand the right-hand side of the recursive equation to: +The code +\begin_inset Formula $f\triangleq k(f)$ +\end_inset + + is clearly invalid. + But if we expand the right-hand side of this recursive definition to: \begin_inset Formula \[ -f\triangleq t\rightarrow k(f)(t) +f\triangleq t\rightarrow k(f)(t)\quad, \] \end_inset @@ -11544,7 +12246,7 @@ instead of \begin_inset Formula $f\triangleq k(f)$ \end_inset -, the code will become valid, and the infinite loop disappears: +, the infinite loop disappears: \begin_inset listings lstparams "mathescape=true" inline false @@ -11721,8 +12423,7 @@ right) $. \begin_layout Plain Layout - implicit val e3 = extractorEither[Z, Z, (R, R => (Z, S))] // - $ + // Extractor for $ \backslash color{dkgreen} Z+R \backslash @@ -11739,8 +12440,12 @@ right) $. \begin_layout Plain Layout - implicit val e4 = extractorFunc[Z, Either[Z, (R, R => (Z, S))], Q] // - $ + implicit val e3 = extractorEither[Z, Z, (R, R => (Z, S))] +\end_layout + +\begin_layout Plain Layout + + // Extractor for $ \backslash color{dkgreen} Q \backslash @@ -11763,6 +12468,11 @@ right) right) $. \end_layout +\begin_layout Plain Layout + + implicit val e4 = extractorFunc[Z, Either[Z, (R, R => (Z, S))], Q] +\end_layout + \begin_layout Plain Layout implicit val e5 = extractorPair[Z, Z, P] // $ @@ -11774,12 +12484,16 @@ times P $. \begin_layout Plain Layout - extractorEither[Z, (Z,P), (Q, Q => Either[Z, (R, R => (Z, S))])] // - Extractor for type $ + // Extractor for type $ \backslash color{dkgreen} K $. \end_layout +\begin_layout Plain Layout + + extractorEither[Z, (Z,P), (Q, Q => Either[Z, (R, R => (Z, S))])] +\end_layout + \begin_layout Plain Layout } @@ -11840,15 +12554,7 @@ Z = String \end_inset - as a way to -\begin_inset Quotes eld -\end_inset - -print -\begin_inset Quotes erd -\end_inset - - values of different types, and it is then called + as a way to print values of different types, and it is then called \begin_inset listings inline true status open @@ -12249,7 +12955,7 @@ status open \end_inset - by a new operation + by a new operation ( \begin_inset listings inline true status open @@ -12261,7 +12967,7 @@ status open \end_inset - constrained to a typeclass called +) constrained to a typeclass called \begin_inset listings inline true status open @@ -27484,8 +28190,12 @@ implicit val bifunctorQ = new Bifunctor[Q] { \begin_layout Plain Layout - def bimap[A, B, C, D](fab: Q[A, B])(f: A => C, g: B => D): Q[C, D] = fab.q - match { + def bimap[A, B, C, D](fab: Q[A, B])(f: A => C, g: B => D): Q[C, D] = +\end_layout + +\begin_layout Plain Layout + + fab.q match { \end_layout \begin_layout Plain Layout @@ -29261,7 +29971,7 @@ status open \begin_layout Plain Layout -def isLong[T]: Boolean +isLong[T]: Boolean \end_layout \end_inset @@ -30293,7 +31003,11 @@ noprefix "false" \end_layout \begin_layout Standard -(dual O'Connor +\begin_inset Foot +status open + +\begin_layout Plain Layout +This property is dual to O'Connor's pointed functor property \begin_inset Index idx status open @@ -30303,7 +31017,26 @@ Russell O'Connor \end_inset -) Assume that a functor +; see Example +\begin_inset space ~ +\end_inset + + +\begin_inset CommandInset ref +LatexCommand ref +reference "subsec:tc-Example-pointed-alternative" +plural "false" +caps "false" +noprefix "false" + +\end_inset + +. +\end_layout + +\end_inset + + Assume that a functor \begin_inset Formula $F$ \end_inset diff --git a/sofp-src/tex/chapter3-picture.pdf b/sofp-src/tex/chapter3-picture.pdf index caf3ffc2c23564fec3368778be68a58c0904394a..a45cbad7df9d32f13fc8a3891a7c4aa065e8a1d6 100644 GIT binary patch delta 200 zcmX?La=>Imod}PGu7O2}p^=rLp_Sp}E)jiHfukbdcn!_d4AK$}Qgtm-ERr^#5M^i5 zvM{hPG%_?aG|@INQa3PA*W}Xo%};SjEJ@XHu`)6+GBJRu*!)xc4P&&ilbNG|n~Ry5 zv4N4XtFw`dlZB&+iKDB9g_*OZp`DG98vzxuTy}O`#U+VFB^5=fXTS FTmbZjGphgq delta 200 zcmX?La=>Imod}P)uAym&p^24=rIqRAE)jiHfukbdcug$KjLi(qQgqD|lTA0D5M^i5 zGB-3eG%>L>HPJROQa3PA*W}Xo%};SjEJ@XHu`)6+GBJRu*!)xc4P&&So0+MLg`26V zrJ=cjp|Po>v7@V_lYz6Di;0t=ft`(!8vzxuTy}O`#U+VFB^5=fXTS FTmZDKGa~>1 diff --git a/sofp-src/tex/sofp-appendices.tex b/sofp-src/tex/sofp-appendices.tex index 6149e710d..828ad7f8c 100644 --- a/sofp-src/tex/sofp-appendices.tex +++ b/sofp-src/tex/sofp-appendices.tex @@ -30,13 +30,13 @@ \section{Summary of notations} \item [{$x^{:A}+\bbnum 0^{:B}$}] \textemdash{} a value of a disjunctive type $A+B$. In Scala, \lstinline!Left(x)! \item [{$A\times B$}] \textemdash{} a product (tuple) type. In Scala, -this type is \lstinline!(A,B)! +this type is \lstinline!(A, B)! \item [{$a^{:A}\times b^{:B}$}] value of a tuple type $A\times B$. In Scala, \lstinline!(a, b)! \item [{$A\rightarrow B$}] \textemdash{} the function type, mapping from $A$ to $B$ \item [{$x^{:A}\rightarrow f$}] \textemdash{} a nameless function (as -a value). In Scala, \lstinline!{ x:A => f }! +a value). In Scala, \lstinline!{ x: A => f }! \item [{$\text{id}$}] \textemdash{} an identity function; in Scala, \lstinline!identity[A]! \item [{$\triangleq$}] \textemdash{} \textsf{``}is defined to be\textsf{''} or \textsf{``}is equal by definition\textsf{''} @@ -62,11 +62,8 @@ \section{Summary of notations} \item [{$\text{flm}_{F},\text{ftn}_{F},\text{pu}_{F}$}] \textemdash{} the standard methods \lstinline!flatMap!, \lstinline!flatten!, and \lstinline!pure! of a monad $F$ -\item [{$F^{\bullet}$}] \textemdash{} the type constructor $F$ understood -as a type-level function. In Scala, \lstinline!F[_]! -\item [{$F^{\bullet}\leadsto G^{\bullet}$}] \textemdash{} or $F\leadsto G$ -a natural transformation between functors $F$ and $G$. In Scala, -\lstinline!F ~> G! +\item [{$F\leadsto G$}] \textemdash{} a natural transformation between +functors $F$ and $G$. In Scala, \lstinline!F ~> G! \item [{$\forall A.\,P^{A}$}] \textemdash{} a universally quantified type expression. In Scala 3, \lstinline![A] => P[A]! \item [{$\exists A.\,P^{A}$}] \textemdash{} an existentially quantified @@ -76,7 +73,7 @@ \section{Summary of notations} \item [{$f\circ g$}] \textemdash{} the backward composition of functions: $f\circ g$ is $x\rightarrow f(g(x))$. In Scala, \lstinline!f compose g! \item [{$F\circ G$}] \textemdash{} the backward composition of type constructors: -$F\circ G$ is $F^{G^{\bullet}}$. In Scala, \lstinline!F[G[A]]! +$(F\circ G)^{A}\triangleq F^{G^{A}}$. In Scala, \lstinline!F[G[A]]! \item [{$\triangleright$}] \textemdash{} use a value as the argument of a function: $x\triangleright f$ is $f(x)$. In Scala, \lstinline!x.pipe(f)! \item [{$f^{\uparrow G}$}] \textemdash{} a function $f$ lifted to a functor @@ -85,17 +82,23 @@ \section{Summary of notations} to $G$ and then to $H$. In Scala, \lstinline!h.map(_.map(f))! \item [{$f^{\downarrow H}$}] \textemdash{} a function $f$ lifted to a contrafunctor $H$ -\item [{$\diamond_{M}$}] \textemdash{} the Kleisli product operation for -the monad $M$ +\item [{$S^{\bullet,T}$}] \textemdash{} the type constructor defined by +fixing one of the type parameters of another type constructor. For +instance: $(S^{\bullet,T})^{A}\triangleq S^{A,T}$. In Scala, \lstinline!S[*, T]!. +Similarly, $(S^{A,\bullet})^{B}\triangleq S^{A,B}$. +\item [{$\diamond_{M}$}] \textemdash{} the Kleisli composition operation +for the monad $M$ \item [{$L\varangle M$}] or equivalently $T_{L}^{M}$ \textemdash{} the monad $L$\textsf{'}s transformer applied to a monad $M$ \item [{$\oplus$}] \textemdash{} the binary operation of a monoid. In Scala, \lstinline!x |+| y! -\item [{$\Delta$}] \textemdash{} the \textsf{``}diagonal\textsf{''} function of type $\forall A.\,A\rightarrow A\times A$ +\item [{$\Delta$}] \textemdash{} the standard \textsf{``}diagonal\textsf{''} function +of type $\forall A.\,A\rightarrow A\times A$ \item [{$\pi_{1},\pi_{2},...$}] \textemdash{} the projections from a tuple to its first, second, ..., parts -\item [{$\boxtimes$}] \textemdash{} pair product of functions: $(f\boxtimes g)(a\times b)\triangleq f(a)\times g(b)$ -\item [{$\boxplus$}] \textemdash{} pair co-product of functions +\item [{$\boxtimes$}] \textemdash{} pair product of functions: $(f\boxtimes g)(a\times b)\triangleq f(a)\times g(b)$, +also applies to relations +\item [{$\boxplus$}] \textemdash{} pair co-product of functions or relations \item [{$\ogreaterthan$}] \textemdash{} pair mapper of relations \item [{$\left[a,b,c\right]$}] \textemdash{} an ordered sequence of values. In Scala, \lstinline!Seq(a, b, c)! @@ -237,26 +240,21 @@ \section{Detailed explanations} $F^{\bullet}$ means the type constructor $F$ understood as a type-level function, \textemdash{} that is, with a type parameter unspecified. In Scala, this is \lstinline!F[_]!. The bullet symbol, $\bullet$, -is used as a placeholder for the missing type parameter. When no type -parameter is needed, $F$ means the same as $F^{\bullet}$. (For example, -\textsf{``}a functor $F$\textsf{''} and \textsf{``}a functor $F^{\bullet}$\textsf{''} mean the same -thing.) However, it is useful for clarity to be able to indicate the -place where the type parameter would appear. For instance, functor -composition is denoted as $F^{G^{\bullet}}$; in Scala 2, this is -\texttt{}\lstinline!Lambda[X => F[G[X]]]! when using the \textsf{``}kind -projector\textsf{''}\index{kind@\textsf{``}kind projector\textsf{''} plugin} plugin.\footnote{\texttt{\href{https://github.com/typelevel/kind-projector}{https://github.com/typelevel/kind-projector}}} -When the type parameter $B$ of a bifunctor $P^{A,B}$ is fixed to -$Z$, we get a functor (with respect to $A$) denoted by $P^{\bullet,Z}$. -Another example: $T_{L}^{M,\bullet}$ denotes a monad transformer -for the base monad $L$ and the foreign monad $M$. The foreign monad -$M$ is a type parameter in $T_{L}^{M,\bullet}$. The symbol $\bullet$ -stands for the transformer\textsf{'}s second type parameter. (The base monad -$L$ is not a type parameter in $T_{L}^{M,\bullet}$ because the construction -of the monad transformer depends on the internal details of $L$.) - -$F^{\bullet}\leadsto G^{\bullet}$ or $F\leadsto G$ means a natural -transformation between two functors $F$ and $G$. In some Scala libraries, -this is denoted by \lstinline!F ~> G!. +is a placeholder for the missing type parameter. When the type parameter +$B$ of a bifunctor $P^{A,B}$ is fixed as $B=Z$, we get a functor +(with respect to $A$) denoted by $P^{\bullet,Z}$. In Scala, this +is written using the \textsf{``}kind projector\textsf{''}\index{kind@\textsf{``}kind projector\textsf{''} plugin} +plugin\footnote{\texttt{\href{https://github.com/typelevel/kind-projector}{https://github.com/typelevel/kind-projector}}} +syntax as \lstinline!Lambda[A => P[A, Z]]!. Another example: $T_{L}^{M,\bullet}$ +denotes a monad transformer for the base monad $L$ and the foreign +monad $M$. The foreign monad $M$ is a type parameter in $T_{L}^{M,\bullet}$. +The symbol $\bullet$ stands for the transformer\textsf{'}s second type parameter. +(The base monad $L$ is not a type parameter in $T_{L}^{M,\bullet}$ +because the construction of the monad transformer depends on the internal +details of $L$.) + +$F\leadsto G$ means a natural transformation between two functors +$F$ and $G$. In some Scala libraries, this is denoted by \lstinline!F ~> G!. $\forall A.\,P^{A}$ is a universally quantified type expression, in which $A$ is a bound type parameter. @@ -329,11 +327,11 @@ \section{Detailed explanations} $H$. For a function $f^{:A\rightarrow B}$, the application of $f^{\downarrow H}$ to a value $h:H^{B}$ is written as $h\triangleright f^{\downarrow H}$ and yields a value of type $H^{A}$. In Scala, this may be written -as \lstinline!h.contramap(f)!. Nested lifting is denoted as, e.g., +as \lstinline!h.contramap(f)!. Repeated lifting is denoted as, e.g., $f^{\downarrow H\uparrow G}\triangleq(f^{\downarrow H})^{\uparrow G}$. -$\diamond_{M}$ means the Kleisli product operation for a given monad -$M$. This is a binary operation working on two Kleisli functions +$\diamond_{M}$ means the Kleisli composition operation for a given +monad $M$. This is a binary operation working on two Kleisli functions of types $A\rightarrow M^{B}$ and $B\rightarrow M^{C}$ and yields a new function of type $A\rightarrow M^{C}$. @@ -766,9 +764,8 @@ \chapter{Parametricity theorem and naturality laws\label{app:Proofs-of-naturalit certain laws derived from the so-called \textsf{``}parametricity theorem\textsf{''}. The most often used laws of this kind are naturality laws for natural transformations, i.e., for functions of type $\forall A.\,F^{A}\rightarrow G^{A}$ -where both $F^{\bullet}$ and $G^{\bullet}$ are functors (or both -contrafunctors). Not having to verify naturality laws by hand saves -a lot of time. +where both $F$ and $G$ are functors (or both contrafunctors). Not +having to verify naturality laws by hand saves a lot of time. Other laws that follow automatically from parametricity are composition laws for functors, commutativity laws for bifunctors, and dinaturality @@ -974,20 +971,20 @@ \subsection{Naturality and dinaturality laws\label{sec:Naturality-laws-for-fully When a function\textsf{'}s type signature is that of a \emph{natural transformation}\index{natural transformation} between functors or between contrafunctors, the parametricity laws have the form derived in Section~\ref{subsec:Naturality-laws-and-natural-transformations}. -If $t:\forall A.\,G^{A}\rightarrow H^{A}$ where $G^{\bullet}$ and -$H^{\bullet}$ are functors then $t$ obeys the naturality law: +If $t:\forall A.\,G^{A}\rightarrow H^{A}$ where $G$ and $H$ are +functors then $t$ obeys the naturality law: \begin{equation} \text{for all }A,B,f^{:A\rightarrow B}\quad:\quad t^{A}\bef f^{\uparrow H}=f^{\uparrow G}\bef t^{B}\quad.\label{eq:naturality-law-for-functors} \end{equation} -If $t:\forall A.\,G^{A}\rightarrow H^{A}$ where $G^{\bullet}$ and -$H^{\bullet}$ are contrafunctors then $t$ obeys the naturality law: +If $t:\forall A.\,G^{A}\rightarrow H^{A}$ where $G$ and $H$ are +contrafunctors then $t$ obeys the naturality law: \begin{equation} \text{for all }A,B,f^{:A\rightarrow B}\quad:\quad f^{\downarrow G}\bef t^{A}=t^{B}\bef f^{\downarrow H}\quad.\label{eq:naturality-law-for-contrafunctors} \end{equation} For example, if we fix the type parameter $A$ in the \lstinline!fmap! method, we obtain a type signature of the form $F^{B}\rightarrow G^{B}$ -where $F^{\bullet}$ and $G^{\bullet}$ are functors: +where $F$ and $G$ are functors: \[ \text{fmap}_{\text{Opt}}^{A,B}:F^{B}\rightarrow G^{B}\quad,\quad\quad F^{B}\triangleq A\rightarrow B\quad,\quad\quad G^{B}\triangleq\text{Opt}^{A}\rightarrow\text{Opt}^{B}\quad. \] @@ -1023,8 +1020,8 @@ \subsection{Naturality and dinaturality laws\label{sec:Naturality-laws-for-fully \[ \text{fold}_{\text{Opt}}^{A,B}:P^{B,B}\quad,\quad\quad\text{where}\quad P^{X,Y}\triangleq X\times(A\times Y\rightarrow X)\rightarrow\text{Opt}^{A}\rightarrow Y\quad. \] -The profunctor $P^{X,Y}$ is complicated but the form of the type -signature ($P^{B,B}$) is simpler. +The profunctor $P$ is complicated but the form of the type signature +($P^{B,B}$) is simpler. Given a fully parametric expression $p$ with the type signature $p:\forall A.\,P^{A,A}$, where $P^{X,Y}$ is some profunctor, one can write the \textbf{wedge @@ -1237,11 +1234,10 @@ \subsection{Uniqueness of functor and contrafunctor liftings\label{sec:Uniquenes the proof of the parametricity theorem significantly depends on the code of those standard implementations. -The structure of a given fully parametric type constructor $F^{A}$ -dictates a unique implementation of a lifting $f^{\uparrow P}$ or -$f^{\downarrow P}$. Let us summarize these implementations for the -six type constructions for the case when $F^{A}$ is covariant in -$A$: +The structure of a given fully parametric type constructor $F$ dictates +a unique implementation of a lifting $f^{\uparrow F}$ or $f^{\downarrow F}$. +Let us summarize these implementations for the six type constructions +for the case when $F^{A}$ is covariant in $A$: \paragraph{Constant type} @@ -1492,13 +1488,12 @@ \section{Relational formulation of parametricity\label{sec:Parametricity-theorem It turns out that any relation $r$ of type $A\leftrightarrow B$ \emph{can} be lifted to a relation (denoted by $r^{\updownarrow P}$) of type $P^{A,A}\leftrightarrow P^{B,B}$. The lifting operation can -be defined for any exponential-polynomial profunctor $P^{\bullet,\bullet}$. -Using that operation, we will prove the \textsf{``}relational parametricity -theorem\textsf{''}: any fully parametric code expression (not necessarily -a function) of type $\forall A.\,P^{A,A}$ satisfies a specially formulated -relational naturality law. That law will then allow us to derive the -wedge law~(\ref{eq:wedge-law-for-profunctors}) and the ordinary -naturality laws. +be defined for any exponential-polynomial profunctor $P$. Using that +operation, we will prove the \textsf{``}relational parametricity theorem\textsf{''}: +any fully parametric code expression (not necessarily a function) +of type $\forall A.\,P^{A,A}$ satisfies a specially formulated relational +naturality law. That law will then allow us to derive the wedge law~(\ref{eq:wedge-law-for-profunctors}) +and the ordinary naturality laws. \subsection{Relations between values of different types} @@ -1658,9 +1653,8 @@ \subsection{Relational product, co-product, and pair mapper. Relational lifting} to a functor $G$ yields a function $f^{\uparrow G}$ of type $G^{A}\rightarrow G^{B}$. Lifting $f$ to a contrafunctor $H$ yields a function $f^{\downarrow H}:H^{B}\rightarrow H^{A}$. Below we will define a lifting of an arbitrary relation $r^{:A\leftrightarrow B}$ -to an arbitrary type constructor $G^{\bullet}$. The lifted relation -will have type $G^{A}\leftrightarrow G^{B}$ and will be denoted by -$r^{\updownarrow G}$. +to an arbitrary type constructor $G$. The lifted relation will have +type $G^{A}\leftrightarrow G^{B}$ and will be denoted by $r^{\updownarrow G}$. Now consider a natural transformation $t:\forall A.\,G^{A}\rightarrow H^{A}$ with its naturality law $f^{\uparrow G}\bef t=t\bef f^{\uparrow H}$. @@ -1743,9 +1737,9 @@ \subsubsection{Definition \label{subsec:Definition-pair-mapper-of-relations}\ref This generalization of the naturality law from functions to relations now has a concise form: The components $t^{A}$ and $t^{B}$ of a natural transformation $t$ belong to \emph{any} relation $r$ lifted -to $t$\textsf{'}s type signature (the type constructor $P^{\bullet}$). This -prepares us for the formulation of the parametricity theorem below. -At the same time, this motivates using the pair mapper operation ($\ogreaterthan$) +to $t$\textsf{'}s type signature (the type constructor $P$). This prepares +us for the formulation of the parametricity theorem below. At the +same time, this motivates using the pair mapper operation ($\ogreaterthan$) to define the lifting a relation to a function type constructor such as $G^{A}\rightarrow H^{A}$. @@ -1955,24 +1949,23 @@ \subsubsection{Example \label{subsec:Example-pair-product-pair-mapper-relation}\ and the property $\text{rev}\left(\text{rev}\,(r)\right)=r$. $\square$ We now turn to defining the relational lifting $r^{\updownarrow G}$ -for an arbitrary type constructor $G^{\bullet}$. It will turn out -that we actually need to define a more general operation: the \emph{simultaneous} -lifting of several relations to a type constructor with several type -parameters. For clarity, we postpone that definition and begin by -lifting a single relation. +for an arbitrary type constructor $G$. It will turn out that we actually +need to define a more general operation: the \emph{simultaneous} lifting +of several relations to a type constructor with several type parameters. +For clarity, we postpone that definition and begin by lifting a single +relation. \subsubsection{Definition \label{subsec:Definition-relational-lifting}\ref{subsec:Definition-relational-lifting} (relational lifting)} Given a relation $r^{:A\leftrightarrow B}$ and a fully parametric -type constructor $G^{\bullet}$, the relational lifting of $r$ to -$G$, denoted by $r^{\updownarrow G}$, is a new relation of type -$G^{A}\leftrightarrow G^{B}$. The relation $r^{\updownarrow G}$ -is defined by induction on the structure of $G$ as shown below in -items \textbf{(a)}\textendash \textbf{(g)}. We will use arbitrary -type constructors $K^{A}$, $L^{A}$, $H^{X,A}$, and $S^{A,R}$ that -are assumed to be fully parametric but not necessarily covariant or -contravariant. +type constructor $G$, the relational lifting of $r$ to $G$, denoted +by $r^{\updownarrow G}$, is a new relation of type $G^{A}\leftrightarrow G^{B}$. +The relation $r^{\updownarrow G}$ is defined by induction on the +structure of $G$ as shown below in items \textbf{(a)}\textendash \textbf{(g)}. +We will use arbitrary type constructors $K$, $L$, $H$, and $S$ +that are assumed to be fully parametric but not necessarily covariant +or contravariant. \textbf{(a)} If $G^{A}\triangleq Z$ with a fixed type $Z$ (different from $A$), we define $r^{\updownarrow G}\triangleq\text{id}^{:Z\leftrightarrow Z}$. @@ -1999,15 +1992,14 @@ \subsubsection{Definition \label{subsec:Definition-relational-lifting}\ref{subse r^{\updownarrow G}\triangleq\big(r,\overline{r^{\uparrow G}}\big)^{\updownarrow S}\quad. \] Here the notation $(r,s)^{\updownarrow S}$ means the \emph{simultaneous} -lifting of the two relations $r$, $s$ to the type constructor $S^{\bullet,\bullet}$ +lifting of the two relations $r$, $s$ to the type constructor $S$ (see Definition~\ref{subsec:Definition-simultaneous-relational-lifting} below). The inductive assumption is that simultaneous liftings to -$S^{\bullet,\bullet}$ are already defined. Also note that we use -$\overline{r^{\updownarrow G}}$ recursively within the definition -of $r^{\updownarrow G}$. This is allowed since we understand $r^{\updownarrow G}$ -to be a function (of type $G^{A}\times G^{B}\rightarrow\bbnum 2$, -see Definition~\ref{subsec:Definition-relation-between-A-B}), and -it is permitted to define functions recursively. +$S$ are already defined. Also note that we use $\overline{r^{\updownarrow G}}$ +recursively within the definition of $r^{\updownarrow G}$. This is +allowed since we understand $r^{\updownarrow G}$ to be a function +(of type $G^{A}\times G^{B}\rightarrow\bbnum 2$, see Definition~\ref{subsec:Definition-relation-between-A-B}), +and it is permitted to define functions recursively. \textbf{(g)} If $G^{A}\triangleq\forall X.\,H^{X,A}$, we define $r^{\updownarrow G}$ of type $(\forall X.\,H^{X,A})\leftrightarrow(\forall Y.\,H^{Y,B})$ @@ -2058,7 +2050,7 @@ \subsubsection{Example \label{subsec:Example-relational-lifting}\ref{subsec:Exam Consider the function graph relation $\left$ of a given function $f^{:A\rightarrow B}$. Use Definition~\ref{subsec:Definition-relational-lifting} to compute the lifting $\left^{\updownarrow P}$ for the -following type constructors $P^{\bullet}$: +following type constructors $P$: \textbf{(a)} $P^{A}\triangleq A+A\times A\quad$. @@ -2082,7 +2074,7 @@ \subsubsection{Example \label{subsec:Example-relational-lifting}\ref{subsec:Exam In each case, the lifted relation $\left^{\updownarrow P}$ has type $P^{A}\leftrightarrow P^{B}$. -\textbf{(a)} At the top level, $P^{A}$ is a disjunction: $P^{A}=\text{Id}^{A}+H^{A}$ +\textbf{(a)} At the top level, $P^{A}$ is a disjunctive type : $P^{A}=\text{Id}^{A}+H^{A}$ where $H^{A}\triangleq A\times A=\text{Id}^{A}\times\text{Id}^{A}$. The lifting to the identity functor is given by $r^{\updownarrow\text{Id}}\triangleq r$ according to Definition~\ref{subsec:Definition-relational-lifting}(b). @@ -2324,13 +2316,12 @@ \subsection{Properties of relational lifting. Simultaneous lifting} We begin by pointing out an ambiguity in applying Definition~\ref{subsec:Definition-relational-lifting}(g) to $G^{A}\triangleq\forall X.\,H^{X,A}$ when $H^{X,A}$ does \emph{not} depend on the type parameter $A$; that is, when $H^{X,A}=K^{X}$ -with some $K^{\bullet}$. In that case, the type constructor $G$ -is a constant functor: $G^{A}=Z\triangleq\forall X.\,K^{X}$. We may -lift a relation $r^{:A\leftrightarrow B}$ to $G^{A}\triangleq Z$ -using Definition~\ref{subsec:Definition-relational-lifting}(a) and -obtain $r^{\updownarrow G}=\text{id}^{:Z\leftrightarrow Z}$. To show -that Definition~\ref{subsec:Definition-relational-lifting} is consistent, -we need to prove that Definitions~\ref{subsec:Definition-relational-lifting}(a) +with some $K$. In that case, the type constructor $G$ is a constant +functor: $G^{A}=Z\triangleq\forall X.\,K^{X}$. We may lift a relation +$r^{:A\leftrightarrow B}$ to $G^{A}\triangleq Z$ using Definition~\ref{subsec:Definition-relational-lifting}(a) +and obtain $r^{\updownarrow G}=\text{id}^{:Z\leftrightarrow Z}$. +To show that Definition~\ref{subsec:Definition-relational-lifting} +is consistent, we need to prove that Definitions~\ref{subsec:Definition-relational-lifting}(a) and~(g) define the same lifted relation $r^{\updownarrow G}$. We will show this is the next statement by using the relational naturality law~(\ref{eq:relational-naturality-law-simplified}). That law is @@ -2390,10 +2381,10 @@ \subsubsection{Statement \label{subsec:Statement-parametricity-gives-identity-re \subsubsection{Definition \label{subsec:Definition-simultaneous-relational-lifting}\ref{subsec:Definition-simultaneous-relational-lifting} (simultaneous relational lifting)} -For any fully parametric type constructor $G^{\bullet,\bullet}$ with -two type parameters, we define the simultaneous lifting of two relations -$r^{:A\leftrightarrow B}$ and $s^{:X\leftrightarrow Y}$ to $G$ -as a new relation denoted by $(r,s)^{\updownarrow G}$ of type $G^{A,X}\leftrightarrow G^{B,Y}$. +For any fully parametric type constructor $G$ with two type parameters, +we define the simultaneous lifting of two relations $r^{:A\leftrightarrow B}$ +and $s^{:X\leftrightarrow Y}$ to $G$ as a new relation denoted by +$(r,s)^{\updownarrow G}$ of type $G^{A,X}\leftrightarrow G^{B,Y}$. We use induction on the structure of $G$: \textbf{(a)} If $G^{A,X}\triangleq Z$ with a fixed type $Z$, we @@ -2402,8 +2393,8 @@ \subsubsection{Definition \label{subsec:Definition-simultaneous-relational-lifti \textbf{(b)} If $G^{A,X}\triangleq A$, we define $(r,s)^{\updownarrow G}\triangleq r$. If $G^{A,X}\triangleq X$, we define $(r,s)^{\updownarrow G}\triangleq s$. -\textbf{(c)} If $K^{\bullet,\bullet}$ and $L^{\bullet,\bullet}$ -are any fully parametric type constructors, we define: +\textbf{(c)} If $K$ and $L$ are any fully parametric type constructors, +we define: \begin{align*} {\color{greenunder}\text{for}\quad G^{A,X}\triangleq K^{A,X}\times L^{A,X}\quad:}\quad & (r,s)^{\updownarrow G}\triangleq(r,s)^{\updownarrow K}\boxtimes(r,s)^{\updownarrow L}\quad;\\ {\color{greenunder}\text{for}\quad G^{A,X}\triangleq K^{A,X}+L^{A,X}\quad:}\quad & (r,s)^{\updownarrow G}\triangleq(r,s)^{\updownarrow K}\boxplus(r,s)^{\updownarrow L}\quad;\\ @@ -2416,7 +2407,7 @@ \subsubsection{Definition \label{subsec:Definition-simultaneous-relational-lifti $L$ are already defined. \textbf{(d)} If $G^{A,X}\triangleq S^{A,X,G^{A,X}}$ is defined recursively -via a recursion scheme $S^{\bullet,\bullet,\bullet}$, we define: +via a recursion scheme $S$, we define: \[ (r,s)^{\updownarrow G}\triangleq\big(r,s,\overline{(r,s)^{\updownarrow G}}\big)^{\updownarrow S}\quad. \] @@ -2424,7 +2415,7 @@ \subsubsection{Definition \label{subsec:Definition-simultaneous-relational-lifti the definition of $(r,s)^{\updownarrow G}$. This is allowed since we understand $(r,s)^{\updownarrow G}$ to be a function, and it is permitted to define functions recursively. The inductive assumption -is that simultaneous liftings of any \emph{three} relations to $S^{\bullet,\bullet,\bullet}$ +is that simultaneous liftings of any \emph{three} relations to $S$ are already defined. \textbf{(e)} If $G^{A,X}\triangleq\forall Z.\,H^{Z,A,X}$, we define @@ -2438,8 +2429,8 @@ \subsubsection{Definition \label{subsec:Definition-simultaneous-relational-lifti \begin{equation} \forall(U,V):\quad\big((r,s)^{\updownarrow\forall Z.\,H^{Z,\bullet,\bullet}}\big)^{U,V}\triangleq\forall w^{:U\leftrightarrow V}.\,(w,r,s)^{\updownarrow H^{\bullet,\bullet,\bullet}}\quad.\label{eq:relational-lifting-quantified-types-short-1} \end{equation} -The inductive assumption is that simultaneous liftings to $H^{\bullet,\bullet,\bullet}$ -are already defined. $\square$ +The inductive assumption is that simultaneous liftings to $H$ are +already defined. $\square$ Parts (d) and (e) of Definition~\ref{subsec:Definition-simultaneous-relational-lifting} use a simultaneous lifting of \emph{three} relations. Comparing Definitions~\ref{subsec:Definition-relational-lifting} @@ -2457,7 +2448,7 @@ \subsubsection{Definition \label{subsec:Definition-simultaneous-relational-lifti defines $r^{\updownarrow G}$ as a relation of type $G^{A}\leftrightarrow G^{B}$, which is the same type as $P^{A,A}\leftrightarrow P^{B,B}$. Second, we may lift the pair of two relations $\left(r,r\right)$ simultaneously -to $P^{\bullet,\bullet}$ according to Definition~\ref{subsec:Definition-simultaneous-relational-lifting} +to $P$ according to Definition~\ref{subsec:Definition-simultaneous-relational-lifting} and obtain another relation $\left(r,r\right)^{\updownarrow P}$ of the same type $P^{A,A}\leftrightarrow P^{B,B}$. The next statement shows that $(r,r)^{\updownarrow P}=r^{\updownarrow G}$. @@ -2472,9 +2463,8 @@ \subsubsection{Statement \label{subsec:Statement-relational-lifting-consistency- \subparagraph{Proof} We enumerate all cases of Definition~\ref{subsec:Definition-simultaneous-relational-lifting} -for the type constructor $P^{\bullet,\bullet}$ and the corresponding -cases of Definition~\ref{subsec:Definition-relational-lifting} for -$G^{\bullet}$. In each case, we will show that $(r,r)^{\updownarrow P}=r^{\updownarrow G}$. +for the type constructor $P$ and the corresponding cases of Definition~\ref{subsec:Definition-relational-lifting} +for $G$. In each case, we will show that $(r,r)^{\updownarrow P}=r^{\updownarrow G}$. If $P^{A,X}\triangleq Z$ with a fixed type $Z$, we have also $G^{A}=Z$. Then $(r,r)^{\updownarrow P}=\text{id}^{:Z\leftrightarrow Z}$ and @@ -2483,30 +2473,29 @@ \subsubsection{Statement \label{subsec:Statement-relational-lifting-consistency- If $P^{A,X}\triangleq A$ or $P^{A,X}=X$, we have $G^{A}=A$. In both cases $(r,r)^{\updownarrow P}\triangleq r$ and $r^{\updownarrow G}=r$. -If $P^{\bullet,\bullet}\triangleq K^{\bullet,\bullet}\times L^{\bullet,\bullet}$ -then we have $G^{A}=K^{A,A}\times L^{A,A}$. Denote $M^{A}\triangleq K^{A,A}$ -and $N^{A}\triangleq L^{A,A}$, so that $G^{A}=M^{A}\times N^{A}$. -The inductive assumptions are $(r,r)^{\updownarrow K}=r^{\updownarrow M}$ -and $(r,r)^{\updownarrow L}=r^{\updownarrow N}$. We find: +If $P\triangleq K\times L$ then we have $G^{A}=K^{A,A}\times L^{A,A}$. +Denote $M^{A}\triangleq K^{A,A}$ and $N^{A}\triangleq L^{A,A}$, +so that $G^{A}=M^{A}\times N^{A}$. The inductive assumptions are +$(r,r)^{\updownarrow K}=r^{\updownarrow M}$ and $(r,r)^{\updownarrow L}=r^{\updownarrow N}$. +We find: \[ (r,r)^{\updownarrow P}=(r,r)^{\updownarrow K}\boxtimes(r,r)^{\updownarrow L}=r^{\updownarrow M}\boxtimes r^{\updownarrow N}=r^{\updownarrow(M\times N)}=r^{\updownarrow G}\quad. \] -If $P^{\bullet,\bullet}\triangleq K^{\bullet,\bullet}+L^{\bullet,\bullet}$ -with $G^{A}=M^{A}+N^{A}$ and the same inductive assumptions, we get: +If $P\triangleq K+L$ with $G^{A}=M^{A}+N^{A}$ and the same inductive +assumptions, we get: \[ (r,r)^{\updownarrow P}=(r,r)^{\updownarrow K}\boxplus(r,r)^{\updownarrow L}=r^{\updownarrow M}\boxplus r^{\updownarrow N}=r^{\updownarrow(M+N)}=r^{\updownarrow G}\quad. \] -If $P^{\bullet,\bullet}\triangleq K^{\bullet,\bullet}\rightarrow L^{\bullet,\bullet}$ -with $G^{A}=M^{A}\rightarrow N^{A}$ and the same inductive assumptions, -we find: +If $P\triangleq K\rightarrow L$ with $G^{A}=M^{A}\rightarrow N^{A}$ +and the same inductive assumptions, we find: \[ (r,r)^{\updownarrow P}=(r,r)^{\updownarrow K}\ogreaterthan(r,r)^{\updownarrow L}=r^{\updownarrow M}\ogreaterthan r^{\updownarrow N}=r^{\updownarrow(M^{\bullet}\rightarrow N^{\bullet})}=r^{\updownarrow G}\quad. \] -If $P^{A,X}\triangleq S^{A,X,P^{A,X}}$ with a recursion scheme $S^{\bullet,\bullet,\bullet}$, -the type constructor $G^{\bullet}$ is defined by: +If $P^{A,X}\triangleq S^{A,X,P^{A,X}}$ with a recursion scheme $S$, +the type constructor $G$ is defined by: \[ G^{A}\triangleq P^{A,A}=S^{A,A,P^{A,A}}=S^{A,A,G^{A}}\quad. \] @@ -2521,7 +2510,7 @@ \subsubsection{Statement \label{subsec:Statement-relational-lifting-consistency- \end{align*} If $P^{A,X}\triangleq\forall Z.\,S^{Z,A,X}$, the type constructor -$G^{\bullet}$ is $G^{A}=\forall Z.\,S^{Z,A,A}$. Denote $Q^{Z,A}\triangleq S^{Z,A,A}$ +$G$ is $G^{A}=\forall Z.\,S^{Z,A,A}$. Denote $Q^{Z,A}\triangleq S^{Z,A,A}$ and obtain $G^{A}=\forall Z.\,Q^{Z,A}$. The lifting $(r,r)^{\updownarrow P}$ is a relation of type $\forall U.\,S^{U,A,A}\leftrightarrow\forall V.\,S^{V,A,A}$ written as: @@ -2566,12 +2555,12 @@ \subsubsection{Definition \label{subsec:Definition-relational-naturality-law}\re (relational naturality law)} Consider any expression $t:\forall A.\,Q^{A}$ containing a single -free variable\index{free variable} $x^{:P^{A}}$, where $P^{\bullet}$ -and $Q^{\bullet}$ are any type constructors. Define the \textbf{binding -function} $t^{\prime}:\forall A.\,P^{A}\rightarrow Q^{A}$ such that -$t=\tilde{t}(x)$ and $\tilde{t}$ has no free variables. (The binding -function describes how the expression $t$ depends on its free variable -$x$.) Then the \textbf{relational naturality law}\index{naturality law!in terms of relations} +free variable\index{free variable} $x^{:P^{A}}$, where $P$ and +$Q$ are any type constructors. Define the \textbf{binding function} +$t^{\prime}:\forall A.\,P^{A}\rightarrow Q^{A}$ such that $t=\tilde{t}(x)$ +and $\tilde{t}$ has no free variables. (The binding function describes +how the expression $t$ depends on its free variable $x$.) Then the +\textbf{relational naturality law}\index{naturality law!in terms of relations} of $t$ is written as: \begin{equation} \forall(A,B).\,\forall r^{:A\leftrightarrow B}.\,(\tilde{t}^{A},\tilde{t}^{B})\in r^{\updownarrow P}\ogreaterthan r^{\updownarrow Q}\quad.\label{eq:relational-naturality-law-1} @@ -2826,7 +2815,7 @@ \subsubsection{Statement \label{subsec:Statement-main-relational-parametricity-1 So, the relational naturality law of $t$ holds. The proof for $t\triangleq\bbnum 0+g$ is analogous. -\paragraph{Use disjunction} +\paragraph{Use disjunctive type} In this case, $t\triangleq\forall A.\,\,\begin{array}{|c||c|} & G^{A}\\ @@ -3072,9 +3061,9 @@ \subsubsection{Statement \label{subsec:Statement-wedge-law-from-parametricity}\r \subsubsection{Statement \label{subsec:Statement-relational-lifting-identity-law}\ref{subsec:Statement-relational-lifting-identity-law}} \textbf{(a)} An identity relation can be removed from a simultaneous -lifting. For instance, given any type constructor $H^{\bullet,\bullet}$, -a fixed type $T$, and any relation $r^{:A\leftrightarrow B}$, we -have: +lifting. For instance, given any type constructor $H$ with two type +parameters, a fixed type $T$, and any relation $r^{:A\leftrightarrow B}$, +we have: \[ (r,\text{id}^{:T\leftrightarrow T})^{\updownarrow H}=r^{\updownarrow G}\quad,\quad\quad\text{where we defined}:\quad G^{A}\triangleq H^{A,T}\quad. \] @@ -3082,14 +3071,14 @@ \subsubsection{Statement \label{subsec:Statement-relational-lifting-identity-law \textbf{(b)} Lifting one or more identity relations produces again an identity relation: \[ -\text{for any }G^{\bullet},H^{\bullet,\bullet},\text{etc}.:\quad(\text{id}^{:A\leftrightarrow A})^{\updownarrow G}=\text{id}^{:G^{A}\leftrightarrow G^{A}}\quad,\quad\quad(\text{id}^{:A\leftrightarrow A},\text{id}^{:X\leftrightarrow X})^{\updownarrow H}=\text{id}^{:H^{A,X}\leftrightarrow H^{A,X}}\quad,\quad\text{etc}. +\text{for any }G,H,\text{etc}.:\quad(\text{id}^{:A\leftrightarrow A})^{\updownarrow G}=\text{id}^{:G^{A}\leftrightarrow G^{A}}\quad,\quad\quad(\text{id}^{:A\leftrightarrow A},\text{id}^{:X\leftrightarrow X})^{\updownarrow H}=\text{id}^{:H^{A,X}\leftrightarrow H^{A,X}}\quad,\quad\text{etc}. \] \subparagraph{Proof} \textbf{(a)} Enumerate all cases of Definition~\ref{subsec:Definition-simultaneous-relational-lifting} -for $H^{\bullet,\bullet}$. +for $H$. If $H^{A,X}\triangleq Z$ with a fixed type $Z$, we have also $G^{A}=Z$. Then $(r,s)^{\updownarrow H}\triangleq\text{id}^{:Z\leftrightarrow Z}$ @@ -3107,8 +3096,8 @@ \subsubsection{Statement \label{subsec:Statement-relational-lifting-identity-law {\color{greenunder}\text{if }H^{A,X}\triangleq K^{A,X}\rightarrow L^{A,X}:}\quad & (r,\text{id})^{\updownarrow H}=(r,\text{id})^{\updownarrow K}\ogreaterthan(r,\text{id})^{\updownarrow L}=r^{\updownarrow K^{\bullet,T}}\ogreaterthan r^{\updownarrow L^{\bullet,T}}=r^{\updownarrow(K^{\bullet,T}\rightarrow L^{\bullet,T})}=r^{\updownarrow G}\quad. \end{align*} -If $H^{A,X}\triangleq S^{A,X,H^{A,X}}$ with a recursion scheme $S^{\bullet,\bullet,\bullet}$, -the type constructor $G^{\bullet}$ is defined by: +If $H^{A,X}\triangleq S^{A,X,H^{A,X}}$ with a recursion scheme $S$, +the type constructor $G$ is defined by: \[ G^{A}\triangleq H^{A,T}=S^{A,T,H^{A,T}}=S^{A,T,G^{A}}\quad. \] @@ -3117,12 +3106,12 @@ \subsubsection{Statement \label{subsec:Statement-relational-lifting-identity-law \begin{align*} & (r,\text{id})^{\updownarrow H}=\big(r,\text{id},\overline{(r,\text{id})^{\updownarrow H}}\big)^{\updownarrow S}\\ {\color{greenunder}\text{inductive assumption }\overline{(r,\text{id})^{\updownarrow H}}=\overline{r^{\updownarrow G}}:}\quad & =\big(r,\gunderline{\text{id}},\overline{r^{\updownarrow G}}\big)^{\updownarrow S}\\ -{\color{greenunder}\text{inductive assumption about lifting id to }S^{\bullet,\bullet,\bullet}:}\quad & =\big(r,\overline{r^{\updownarrow G}}\big)^{\updownarrow S^{\bullet,T,\bullet}}\\ +{\color{greenunder}\text{inductive assumption about lifting id to }S:}\quad & =\big(r,\overline{r^{\updownarrow G}}\big)^{\updownarrow S^{\bullet,T,\bullet}}\\ {\color{greenunder}\text{definition of lifting to }G:}\quad & =r^{\updownarrow G}\quad. \end{align*} If $H^{A,X}\triangleq\forall Z.\,S^{Z,A,X}$, the type constructor -$G^{\bullet}$ is $G^{A}=\forall Z.\,S^{Z,A,T}$. The lifting $(r,\text{id})^{\updownarrow H}$ +$G$ is $G^{A}=\forall Z.\,S^{Z,A,T}$. The lifting $(r,\text{id})^{\updownarrow H}$ is a relation of type $\forall U.\,S^{U,A,X}\leftrightarrow\forall V.\,S^{V,A,X}$ written as: \begin{align*} @@ -3131,12 +3120,11 @@ \subsubsection{Statement \label{subsec:Statement-relational-lifting-identity-law {\color{greenunder}\text{definition of lifting to }G:}\quad & =(r^{\updownarrow G})^{U,V}\quad. \end{align*} Here we used the inductive assumption that identity relations may -be omitted from liftings to $S^{\bullet,\bullet,\bullet}$. +be omitted from liftings to $S$. \textbf{(b)} Enumerate all cases of Definition~\ref{subsec:Definition-relational-lifting} -for $G^{\bullet}$ or Definition~\ref{subsec:Definition-simultaneous-relational-lifting} -for $H^{\bullet,\bullet}$. The proofs are similar, so we will only -prove that $(\text{id},\text{id})^{\updownarrow H}=\text{id}$. +for $G$ or Definition~\ref{subsec:Definition-simultaneous-relational-lifting} +for $H$. The proofs are similar, so we will only prove that $(\text{id},\text{id})^{\updownarrow H}=\text{id}$. If $H^{A,X}\triangleq Z$ with a fixed type $Z$, we have $(r,s)^{\updownarrow H}\triangleq\text{id}^{:Z\leftrightarrow Z}$ for any relations $r$, $s$. @@ -3148,9 +3136,9 @@ \subsubsection{Statement \label{subsec:Statement-relational-lifting-identity-law The next three similar cases use the inductive assumptions $(\text{id},\text{id})^{\updownarrow K}=\text{id}$ and $(\text{id},\text{id})^{\updownarrow L}=\text{id}$: \begin{align*} -{\color{greenunder}\text{if }H^{\bullet,\bullet}\triangleq K^{\bullet,\bullet}\times L^{\bullet,\bullet}:}\quad & (\text{id},\text{id})^{\updownarrow H}=(\text{id},\text{id})^{\updownarrow K}\boxtimes(\text{id},\text{id})^{\updownarrow L}=\text{id}\boxtimes\text{id}\quad;\\ -{\color{greenunder}\text{if }H^{\bullet,\bullet}\triangleq K^{\bullet,\bullet}+L^{\bullet,\bullet}:}\quad & (\text{id},\text{id})^{\updownarrow H}=(\text{id},\text{id})^{\updownarrow K}\boxplus(\text{id},\text{id})^{\updownarrow L}=\text{id}\boxplus\text{id}\quad;\\ -{\color{greenunder}\text{if }H^{\bullet,\bullet}\triangleq K^{\bullet,\bullet}\rightarrow L^{\bullet,\bullet}:}\quad & (\text{id},\text{id})^{\updownarrow H}=(\text{id},\text{id})^{\updownarrow K}\ogreaterthan(\text{id},\text{id})^{\updownarrow L}=\text{id}\ogreaterthan\text{id}\quad. +{\color{greenunder}\text{if }H\triangleq K\times L:}\quad & (\text{id},\text{id})^{\updownarrow H}=(\text{id},\text{id})^{\updownarrow K}\boxtimes(\text{id},\text{id})^{\updownarrow L}=\text{id}\boxtimes\text{id}\quad;\\ +{\color{greenunder}\text{if }H\triangleq K+L:}\quad & (\text{id},\text{id})^{\updownarrow H}=(\text{id},\text{id})^{\updownarrow K}\boxplus(\text{id},\text{id})^{\updownarrow L}=\text{id}\boxplus\text{id}\quad;\\ +{\color{greenunder}\text{if }H\triangleq K\rightarrow L:}\quad & (\text{id},\text{id})^{\updownarrow H}=(\text{id},\text{id})^{\updownarrow K}\ogreaterthan(\text{id},\text{id})^{\updownarrow L}=\text{id}\ogreaterthan\text{id}\quad. \end{align*} It follows from Example~\ref{subsec:Example-pair-product-pair-mapper-relation} with $f=\text{id}$ and $g=\text{id}$ that: @@ -3159,7 +3147,7 @@ \subsubsection{Statement \label{subsec:Statement-relational-lifting-identity-law \] So, in all three cases we obtain: $(\text{id},\text{id})^{\updownarrow H}=\text{id}$. -If $H^{A,X}\triangleq S^{A,X,H^{A,X}}$ with a recursion scheme $S^{\bullet,\bullet,\bullet}$, +If $H^{A,X}\triangleq S^{A,X,H^{A,X}}$ with a recursion scheme $S$, we have: \[ (\text{id},\text{id})^{\updownarrow H}=\big(\text{id},\text{id},\overline{(\text{id},\text{id})^{\updownarrow H}}\big)^{\updownarrow S}=(\text{id},\text{id},\text{id})^{\updownarrow S}\quad, @@ -3167,7 +3155,7 @@ \subsubsection{Statement \label{subsec:Statement-relational-lifting-identity-law because by the inductive assumption the identity law holds for the recursive call: $\overline{(\text{id},\text{id})^{\updownarrow H}}=\text{id}$. Another inductive assumption is that the identity law holds for the -liftings to $S^{\bullet,\bullet,\bullet}$. So, we get: +liftings to $S$. So, we get: \[ (\text{id},\text{id})^{\updownarrow H}=(\text{id},\text{id},\text{id})^{\updownarrow S}=\text{id}\quad. \] @@ -3240,10 +3228,10 @@ \subsection{Strong dinaturality: definition and general properties\label{subsec: \subsubsection{Statement \label{subsec:Statement-profunctor-pushout-entails-lifted-f}\ref{subsec:Statement-profunctor-pushout-entails-lifted-f}} -We assume a fully parametric profunctor $P^{\bullet,\bullet}$, arbitrary -types $X$, $Y$, and arbitrary values $x^{:P^{X,X}}$, $y^{:P^{Y,Y}}$, -and $f^{:X\rightarrow Y}$. If $(x,y)\in\text{push}\,(f^{\downarrow P},f^{\uparrow P})$ -then $(x,y)\in\left^{\updownarrow T}$ where $T^{A}\triangleq P^{A,A}$. +We assume a fully parametric profunctor $P$, arbitrary types $X$, +$Y$, and arbitrary values $x^{:P^{X,X}}$, $y^{:P^{Y,Y}}$, and $f^{:X\rightarrow Y}$. +If $(x,y)\in\text{push}\,(f^{\downarrow P},f^{\uparrow P})$ then +$(x,y)\in\left^{\updownarrow T}$ where $T^{A}\triangleq P^{A,A}$. \subparagraph{Proof} @@ -3439,13 +3427,13 @@ \subsubsection{Statement \label{subsec:Statement-strong-dinaturality-entails-din \end{align*} \textbf{(b)} Consider the case where $F$ and $G$ are both functors. -We may view $t:F^{A}\rightarrow G^{A}$ as a function with the type -signature $t:P^{A,A}\rightarrow Q^{A,A}$ if we define the profunctors -$P^{X,Y}\triangleq F^{Y}$ and $Q^{X,Y}\triangleq G^{Y}$. Since $P^{X,Y}$ -and $Q^{X,Y}$ ignore the type parameter $X$, we have the liftings -$f^{\uparrow P}=f^{\uparrow F}$, $f^{\downarrow P}=\text{id}$, $f^{\uparrow Q}=f^{\uparrow G}$, -and $f^{\downarrow Q}=\text{id}$. The strong dinaturality law of -$t$ is then written as: +We may view $t^{A}:F^{A}\rightarrow G^{A}$ as a function with the +type signature $t:\forall A.\,P^{A,A}\rightarrow Q^{A,A}$ if we define +the profunctors $P^{X,Y}\triangleq F^{Y}$ and $Q^{X,Y}\triangleq G^{Y}$. +Since $P^{X,Y}$ and $Q^{X,Y}$ ignore the type parameter $X$, we +have the liftings $f^{\uparrow P}=f^{\uparrow F}$, $f^{\downarrow P}=\text{id}$, +$f^{\uparrow Q}=f^{\uparrow G}$, and $f^{\downarrow Q}=\text{id}$. +The strong dinaturality law of $t$ is then written as: \[ \text{when}\quad x^{:F^{A}}\triangleright f^{\uparrow F}=y^{:F^{B}}\quad\text{ then}\quad x\triangleright t\triangleright f^{\uparrow G}\overset{?}{=}y\triangleright t\quad. \] @@ -3933,11 +3921,11 @@ \subsubsection{Example \label{subsec:Example-strong-dinaturality-for-some-type-s \subparagraph{Solution} \textbf{(a)} We can represent the type signature as $\forall A.\,K^{A,A}\rightarrow L^{A,A}$ -with some profunctor $K^{X,Y}$. Then we will need to consider two -cases where $F$ is either covariant or contravariant. If $F$ is -covariant, we define $K^{X,Y}\triangleq F^{Y}$. If $F$ is contravariant, -we define $K^{X,Y}\triangleq F^{X}$. In every case, $K$ depends -on only one of its type parameters. By Statement~\ref{subsec:Statement-post-wedge}(a), +with some profunctor $K$. Then we will need to consider two cases +where $F$ is either covariant or contravariant. If $F$ is covariant, +we define $K^{X,Y}\triangleq F^{Y}$. If $F$ is contravariant, we +define $K^{X,Y}\triangleq F^{X}$. In every case, $K$ depends on +only one of its type parameters. By Statement~\ref{subsec:Statement-post-wedge}(a), $K$ will have the post-wedge property. Strong dinaturality will then follow from Statement~\ref{subsec:Statement-post-wedge-entails-strong-dinaturality}. @@ -4060,8 +4048,8 @@ \subsection{Strong dinaturality of \texttt{foldFn}} \subsubsection{Statement \label{subsec:Statement-functor-post-pre-wedge}\ref{subsec:Statement-functor-post-pre-wedge}} -If $L^{\bullet}$ is a polynomial functor and $K^{\bullet,\bullet}$ -is a profunctor with the post-wedge property then the profunctor $P^{X,Y}\triangleq L^{K^{X,Y}}$ +If $L$ is a polynomial functor and $K$ is a profunctor with the +post-wedge property then the profunctor $P^{X,Y}\triangleq L^{K^{X,Y}}$ also has the post-wedge property. \subparagraph{Proof} @@ -4090,16 +4078,16 @@ \subsubsection{Statement \label{subsec:Statement-functor-post-pre-wedge}\ref{sub \subsubsection{Statement \label{subsec:Statement-functor-composition-relational-lifting}\ref{subsec:Statement-functor-composition-relational-lifting}} -For any type constructors $G^{\bullet}$ and $H^{\bullet}$, define -$F\triangleq G\circ H$, equivalently denoted as $F^{A}\triangleq G^{H^{A}}$. -Then the lifting of any relation $r^{:A\leftrightarrow B}$ to $F^{\bullet}$ -can be expressed as $r^{\updownarrow F}=(r^{\updownarrow H})^{\updownarrow G}$, -which we may write more concisely as $r^{\updownarrow F}=r^{\updownarrow H\updownarrow G}$. +For any type constructors $G$ and $H$, define $F\triangleq G\circ H$, +equivalently denoted as $F^{A}\triangleq G^{H^{A}}$. Then the lifting +of any relation $r^{:A\leftrightarrow B}$ to $F$ can be expressed +as $r^{\updownarrow F}=(r^{\updownarrow H})^{\updownarrow G}$, which +we may write more concisely as $r^{\updownarrow F}=r^{\updownarrow H\updownarrow G}$. \subparagraph{Proof} For each case of Definition~\ref{subsec:Definition-relational-lifting} -for the type constructor $G^{\bullet}$, we show that $r^{\updownarrow F}=(r^{\updownarrow H})^{\updownarrow G}$. +for the type constructor $G$, we show that $r^{\updownarrow F}=(r^{\updownarrow H})^{\updownarrow G}$. \paragraph{Constant type} @@ -4139,7 +4127,7 @@ \subsubsection{Statement \label{subsec:Statement-functor-composition-relational- With $G^{A}\triangleq S^{A,G^{A}}$, we have $F^{A}=S^{H^{A},G^{H^{A}}}=S^{H^{A},F^{A}}$. Denoting $Q^{A,B}\triangleq S^{H^{A},B}$, we can write $F^{A}=Q^{A,F^{A}}$. One inductive assumption is that Statement~\ref{subsec:Statement-functor-composition-relational-lifting} -already holds separately with respect to each type parameter of $S^{\bullet,\bullet}$ +already holds separately with respect to each type parameter of $S$ and, in particular, a simultaneous lifting to $Q$ satisfies: \[ (r,s)^{\updownarrow P}=(r,s)^{\updownarrow S^{H^{\bullet},\bullet}}=(r^{\updownarrow H},s)^{\updownarrow S^{\bullet,\bullet}}\quad. @@ -4156,7 +4144,7 @@ \subsubsection{Statement \label{subsec:Statement-functor-composition-relational- With $G^{A}\triangleq\forall X.\,P^{X,A}$, we have $F^{A}=\forall X.\,P^{X,H^{A}}$. We may assume that Statement~\ref{subsec:Statement-functor-composition-relational-lifting} already holds separately for liftings with respect to each type parameter -of $P^{\bullet,\bullet}$. In particular, if we denote $Q^{X,B}\triangleq P^{X,H^{B}}$ +of $P$. In particular, if we denote $Q^{X,B}\triangleq P^{X,H^{B}}$ then: \[ (s,r)^{\updownarrow Q}=(s,r)^{\updownarrow P^{\bullet,H^{\bullet}}}=(s,r^{\updownarrow H})^{\updownarrow P}\quad. @@ -4268,8 +4256,8 @@ \subsubsection{Statement \label{subsec:Statement-pullback-lifted-to-functor}\ref \paragraph{Recursive types} -If $F^{A}\triangleq S^{A,F^{A}}$ where $S^{\bullet,\bullet}$ is -a polynomial bifunctor then: +If $F^{A}\triangleq S^{A,F^{A}}$ where $S$ is a polynomial bifunctor +then: \[ r^{\updownarrow F}=\big(r,\overline{r^{\updownarrow F}}\big)^{\updownarrow S}\quad,\quad\quad f^{\uparrow F}=f^{\uparrow S^{\bullet,F^{A}}}\bef\big(\overline{f^{\uparrow F}}\big)^{\uparrow S^{C,\bullet}}\quad,\quad\quad g^{\uparrow F}=g^{\uparrow S^{\bullet,F^{B}}}\bef\big(\overline{g^{\uparrow F}}\big)^{\uparrow S^{C,\bullet}}\quad. \] @@ -4430,12 +4418,12 @@ \subsubsection{Statement \label{subsec:Statement-undisjunctive-type-constructors \end{equation} By definition of the relational co-product $r^{\updownarrow Q}\boxplus r^{\updownarrow R}$, the values $p(x)$ and $q(y)$ must be in the same part of the disjunctive -type $Q^{\bullet}+R^{\bullet}$. By assumption, $P^{A}\neq\bbnum 0$ -and $P^{B}\neq\bbnum 0$, so we may choose some values $(x_{0}^{:P^{A}},y_{0}^{:P^{B}})$ -and we will then have $\left(p(x_{0}),q(y_{0})\right)\in r^{\updownarrow Q}\boxplus r^{\updownarrow R}$. +type $Q^{...}+R^{...}$. By assumption, $P^{A}\neq\bbnum 0$ and $P^{B}\neq\bbnum 0$, +so we may choose some values $(x_{0}^{:P^{A}},y_{0}^{:P^{B}})$ and +we will then have $\left(p(x_{0}),q(y_{0})\right)\in r^{\updownarrow Q}\boxplus r^{\updownarrow R}$. It follows that the values $p(x_{0})$ and $q(y_{0})$ must belong -to the same part of the type $Q^{\bullet}+R^{\bullet}$. Suppose they -belong to the part $Q^{\bullet}+\bbnum 0$, so that $p(x_{0})=s_{0}^{:Q^{A}}+\bbnum 0^{:R^{A}}$ +to the same part of the type $Q^{...}+R^{...}$. Suppose they belong +to the part $Q^{...}+\bbnum 0$, so that $p(x_{0})=s_{0}^{:Q^{A}}+\bbnum 0^{:R^{A}}$ and $q(y_{0})=t_{0}^{:Q^{B}}+\bbnum 0^{:R^{B}}$ with some values $s_{0}$ and $t_{0}$. The relational law~(\ref{eq:relational-law-example-all-x-y}) holds for all $x$ and $y$, so: @@ -4455,8 +4443,8 @@ \subsubsection{Statement \label{subsec:Statement-undisjunctive-type-constructors for all types $B$. The other possibility is that both values $p(x_{0})$ and $q(y_{0})$ -belong to the second part ($\bbnum 0+R^{\bullet}$) of the type $Q^{\bullet}+R^{\bullet}$. -A similar argument shows that $p(x)$ and $q(y)$ are of type $\bbnum 0+R^{\bullet}$ +belong to the right part ($\bbnum 0+R^{...}$) of the type $Q^{...}+R^{...}$. +A similar argument shows that $p(x)$ and $q(y)$ are of type $\bbnum 0+R^{...}$ for all $x$ and $y$. So, the functions $p$ and $q$ are equivalent to functions of types $P^{A}\rightarrow\bbnum 0^{:Q^{A}}+R^{A}$ and $P^{B}\rightarrow\bbnum 0^{:Q^{B}}+R^{B}$. @@ -4478,9 +4466,9 @@ \subsubsection{Statement \label{subsec:Statement-undisjunctive-type-constructors A type constructor $P$ is lifting-to-full when: -\textbf{(b)} $P^{A}\triangleq\bbnum 1$. +\textbf{(b)} $P^{A}\triangleq\bbnum 1$$\quad$. -\textbf{(c)} $P^{A}\triangleq A$. +\textbf{(c)} $P^{A}\triangleq A$$\quad$. \textbf{(d)} $P^{A}\triangleq K^{A}\rightarrow L^{A}$ where $K$ is arbitrary and $L$ is lifting-to-full. @@ -4502,10 +4490,10 @@ \subsubsection{Statement \label{subsec:Statement-undisjunctive-type-constructors \subparagraph{Proof} -\textbf{(a)} Consider $P^{A}\triangleq K^{A}\times L^{A}$ where both -$K^{A}\triangleq A$ and $L^{A}\triangleq A\rightarrow\bbnum 2$. -Both $K$ and $L$ are non-disjunctive by this statement\textsf{'}s items \textbf{(c)} -and \textbf{(d)}. However, the type $\forall A.\,A\times(A\rightarrow\bbnum 2)\rightarrow Q^{A}+R^{A}$ +\textbf{(a)} Consider $P^{A}\triangleq K^{A}\times L^{A}$ where $K^{A}\triangleq A$ +and $L^{A}\triangleq A\rightarrow\bbnum 2$. Both $K$ and $L$ are +non-disjunctive by this statement\textsf{'}s items \textbf{(c)} and \textbf{(d)}. +However, the type $\forall A.\,A\times(A\rightarrow\bbnum 2)\rightarrow Q^{A}+R^{A}$ admits functions that do not always return values of types $Q^{A}+\bbnum 0$ or $\bbnum 0+R^{A}$. A counter-example is: \begin{align*} @@ -5204,6 +5192,30 @@ \subsubsection*{Exercise \ref{subsec:Exercise-filterable-laws-4}} \addsec{Chapter \ref{chap:Semimonads-and-monads}} +\subsubsection*{Exercise \ref{subsec:Exercise-1-monads-3}} + +As a counterexample, choose $S=$ \lstinline!Option! and define $g$ +as: +\begin{lstlisting} +def g: Option[Int] => Option[Int] = { _ => Some(123) } +\end{lstlisting} +\[ +g:\bbnum 1+\text{Int}\rightarrow\bbnum 1+\text{Int}\quad,\quad\quad g\triangleq\_\rightarrow\bbnum 0+123^{:\text{Int}}\quad. +\] +Then apply the functions $g^{\uparrow S}\bef\text{ftn}_{S}$ and $\text{ftn}_{S}\bef g$ +to the value \lstinline!x = None! of type \lstinline!Option[Option[Int]]! +(in the code notation, $x\triangleq\bbnum 1+\bbnum 0^{:\bbnum 1+\text{Int}}$): +\begin{lstlisting} +val x: Option[Option[Int]] = None +assert(x.map(g).flatten == None) +assert(g(x.flatten) == Some(123)) +\end{lstlisting} +\begin{align*} + & x\triangleright g^{\uparrow S}\bef\text{ftn}_{S}=(\bbnum 1+\bbnum 0^{:\bbnum 1+\text{Int}})\triangleright\text{ftn}_{S}=\bbnum 1+\bbnum 0^{:\text{Int}}\quad,\\ + & x\triangleright\text{ftn}_{S}\bef g=(\bbnum 1+\bbnum 0^{:\text{Int}})\triangleright g=\bbnum 0+123^{:\text{Int}}\quad. +\end{align*} +We find that $g^{\uparrow S}\bef\text{ftn}_{S}\neq\text{ftn}_{S}\bef g$. + \subsubsection*{Exercise \ref{subsec:Exercise-1-monads-3-1}} \index{monads!List monad with empty sub-lists@\texttt{List} monad with empty sub-lists}The diff --git a/sofp-src/tex/sofp-applicative.tex b/sofp-src/tex/sofp-applicative.tex index 5d512b67f..a2aafe74b 100644 --- a/sofp-src/tex/sofp-applicative.tex +++ b/sofp-src/tex/sofp-applicative.tex @@ -11,10 +11,10 @@ \section{Motivation and first examples} In previous chapters, we generalized the \lstinline!map!, \lstinline!filter!, and \lstinline!flatMap! methods from sequences to other type constructors -that support such methods obeying suitable laws. Following the same +that support those methods obeying suitable laws. Following the same path, we now turn to the \lstinline!zip! method. Although \lstinline!zip! is most often used with sequences, many other type constructors can -also have a suitable \lstinline!zip! method. Those type constructors +also have a lawful \lstinline!zip! method. Those type constructors are known as\textbf{ applicative}. \subsection{Generalizing the \texttt{zip} method from sequences to other types} diff --git a/sofp-src/tex/sofp-back-cover-no-bg.tex b/sofp-src/tex/sofp-back-cover-no-bg.tex index 61d00eb6d..b1456e388 100644 --- a/sofp-src/tex/sofp-back-cover-no-bg.tex +++ b/sofp-src/tex/sofp-back-cover-no-bg.tex @@ -25,10 +25,10 @@ practical applications of parametricity. Long and difficult, yet boring explanations are logically -developed in excruciating detail through 1892 -Scala code snippets, 191 statements with step-by-step -derivations, 103 diagrams, 223 examples -with tested Scala code, and 308 exercises. Discussions +developed in excruciating detail through 1905 +Scala code snippets, 192 statements with step-by-step +derivations, 104 diagrams, 223 examples +with tested Scala code, and 310 exercises. Discussions build upon each chapter\textsf{'}s material further. Beginners in FP will find tutorials about the \texttt{map}/\texttt{reduce} diff --git a/sofp-src/tex/sofp-curry-howard.tex b/sofp-src/tex/sofp-curry-howard.tex index 1e3f08c62..85d1ed584 100644 --- a/sofp-src/tex/sofp-curry-howard.tex +++ b/sofp-src/tex/sofp-curry-howard.tex @@ -691,8 +691,8 @@ \subsubsection{Example \label{subsec:Example-ch-notation-function-2}\ref{subsec: \subsubsection{Example \label{subsec:Example-ch-notation-function-3}\ref{subsec:Example-ch-notation-function-3}} -Define a Scala type constructor \lstinline!F[A]! corresponding to -the type notation: +Define a Scala type constructor \lstinline!F! corresponding to the +type notation: \[ F^{A}\triangleq\bbnum 1+\text{Int}\times A\times A+\text{Int}\times\left(\text{Int}\rightarrow A\right)\quad. \] @@ -3893,7 +3893,7 @@ \section{Summary} The constructive propositional logic with the rules listed in Table~\ref{tab:Proof-rules-for-constructive-logic} is \textbf{decidable},\index{decidable logic} i.e., it has an algorithm that either finds a proof or disproves any given formula. However, -that logic cannot handle type constructors such as $\text{Array}^{A}$. +that logic cannot handle type constructors such as \lstinline!Array!. It also cannot handle premises containing type quantifiers such as $\forall(A,B)$, because all the available logic rules have the quantifiers placed \emph{outside} the premises. @@ -4261,7 +4261,7 @@ \subsubsection{Example \label{subsec:ch-solvedExample-5}\ref{subsec:ch-solvedExa \subparagraph{Solution} -Begin by defining a type alias for the type constructor $\text{Reader}^{E,A}$: +Begin by defining a type alias for the type $\text{Reader}^{E,A}$: \begin{lstlisting} type Reader[E, A] = E => A \end{lstlisting} @@ -4477,8 +4477,8 @@ \subsubsection{Example \label{subsec:ch-solvedExample-8}\ref{subsec:ch-solvedExa \subparagraph{Solution} -For a type constructor, say, $P^{A}$, the standard type signatures -for \lstinline!map! and \lstinline!flatMap! are: +For a type constructor, say, $P$, the standard type signatures for +\lstinline!map! and \lstinline!flatMap! are: \[ \text{map}:P^{A}\rightarrow(A\rightarrow B)\rightarrow P^{B}\quad,\quad\quad\text{flatMap}:P^{A}\rightarrow(A\rightarrow P^{B})\rightarrow P^{B}\quad. \] @@ -4716,14 +4716,14 @@ \subsubsection{Exercise \label{subsec:ch-Exercise-3}\ref{subsec:ch-Exercise-3}} \subsubsection{Exercise \label{subsec:ch-Exercise-4}\ref{subsec:ch-Exercise-4}} -Implement the \lstinline!map! function for the type constructor \lstinline!P[A]! +Implement the \lstinline!map! function for the type constructor \lstinline!P! from Example~\ref{subsec:ch-solvedExample-2}. The required type signature is $P^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow P^{B}$. Preserve information as much as possible. \subsubsection{Exercise \label{subsec:ch-Exercise-4-1}\ref{subsec:ch-Exercise-4-1}} -For the type constructor $Q^{T,A}$ defined in Exercise~\ref{subsec:Exercise-type-notation-1}, +For the type constructor $Q$ defined in Exercise~\ref{subsec:Exercise-type-notation-1}, define the \lstinline!map! function, preserving information as much as possible: \[ diff --git a/sofp-src/tex/sofp-essay1.tex b/sofp-src/tex/sofp-essay1.tex index fb76f9322..486aba834 100644 --- a/sofp-src/tex/sofp-essay1.tex +++ b/sofp-src/tex/sofp-essay1.tex @@ -106,8 +106,8 @@ It is interesting to note that most older programming languages (C/C++, Java, JavaScript, Python) do not support some of these composite types. -In other words, these programming languages have type systems based -on an incomplete logic. As a result, users of these languages have +In other words, those programming languages have type systems based +on an incomplete logic. As a result, users of those languages have to implement burdensome workarounds that make for error-prone code. Failure to follow mathematical principles has real costs (Figure~\ref{fig:The-Pyongyang-method-of-error-free-programming-1}). @@ -159,7 +159,7 @@ The languages from the \textsf{``}industrial\textsf{''} group are based on existing and mature software ecosystems: F\# on .NET, Scala on JVM, and Swift on the MacOS/iOS platform. One of the important design requirements -for these languages is 100\% binary compatibility with their \textsf{``}parent\textsf{''} +for those languages is 100\% binary compatibility with their \textsf{``}parent\textsf{''} platform\textsf{'}s languages (F\# with C\#, Scala with Java, and Swift with Objective-C). Because of this, developers can immediately take advantage of the existing tooling, package management, and industry-strength @@ -174,10 +174,11 @@ functional paradigm is also smoother for software developers because F\#, Scala, and Swift seamlessly support the familiar object-oriented programming\index{object-oriented programming} paradigm. At the same -time, these new languages still have logically complete type systems, -which gives developers an important benefit of type-safe domain modeling. +time, those new \textsf{``}industrial\textsf{''} functional languages still have logically +complete type systems, which gives developers an important benefit +of type-safe domain modeling. -Nevertheless, the type systems of these languages are not equally +Nevertheless, the type systems of those languages are not equally powerful. For instance, F\# and Swift are similar to OCaml in many ways but omit OCaml\textsf{'}s parameterized modules and some other features. Of all the mentioned languages, only Scala and Haskell directly support diff --git a/sofp-src/tex/sofp-essay2.tex b/sofp-src/tex/sofp-essay2.tex index f51a02b3d..8697edf6a 100644 --- a/sofp-src/tex/sofp-essay2.tex +++ b/sofp-src/tex/sofp-essay2.tex @@ -376,9 +376,9 @@ \subsection*{Programmers avoid academic terminology } writing code. Looking at the situation in construction business in the U.S.A., we -find that it employs about $10$ times more construction workers as +find that it employs about $40$ times more construction workers as architects. We might conclude that perhaps one software engineer is -required per dozen software artisans. +required per several dozen software artisans. What is the price of \emph{not} having engineers, of replacing them with artisans? diff --git a/sofp-src/tex/sofp-free-type.tex b/sofp-src/tex/sofp-free-type.tex index 4c0320cc4..719dec56f 100644 --- a/sofp-src/tex/sofp-free-type.tex +++ b/sofp-src/tex/sofp-free-type.tex @@ -2565,13 +2565,13 @@ \subsection{Free constructions for $P$-typeclasses\label{subsec:Free-constructio \subsubsection{Definition \label{subsec:Definition-f-algebra}\ref{subsec:Definition-f-algebra}} Given a functor $F$, a type $M$ is called an $F$\textbf{-algebra} -if there exists a morphism $p_{M}:F^{M}\rightarrow M$. The type $M$ -is the \textbf{carrier} and the morphism $p_{M}$ is the \index{structure map of $F$-algebra}\textbf{structure -map} of the $F$-algebra. \index{$F$-algebra!structure map} All $F$-algebras -form a category whose objects are pairs $\left(M,p_{M}\right)$ and -morphisms are defined as functions $f^{:M\rightarrow N}$ satisfying +if\index{$F$-algebra!see functor algebra} there exists a morphism +$p_{M}:F^{M}\rightarrow M$. The morphism $p_{M}$ is called the \index{structure map of functor algebra}\textbf{structure +map} of the $F$-algebra. \index{functor algebra!structure map} All +$F$-algebras form a category whose objects are pairs $\left(M,p_{M}\right)$ +and morphisms are defined as functions $f^{:M\rightarrow N}$ satisfying Eq.~(\ref{eq:p-algebra-morphism-law}) with $P=F$. Such functions -$f$ are called $F$-\textbf{algebra morphisms}.\index{$F$-algebra!morphism} +$f$ are called $F$-\textbf{algebra morphisms}.\index{functor algebra!morphism} $\square$ To show that the category laws hold for $F$-algebras, we use the @@ -4736,18 +4736,18 @@ \subsubsection{Exercise \label{subsec:Exercise-free-type-4}\ref{subsec:Exercise- \subsubsection{Exercise \label{subsec:Exercise-free-type-5}\ref{subsec:Exercise-free-type-5}} -Describe the monoid type class via a method functor $C^{\bullet}$ -(such that the monoid\textsf{'}s operations are combined into the type $S^{M}\rightarrow M$). -Using $S^{\bullet}$, implement the free monoid on a type $Z$ in -the Church encoding. +Describe the monoid type class via a method functor $C$ (such that +the monoid\textsf{'}s operations are combined into the type $S^{M}\rightarrow M$). +Using $S$, implement the free monoid on a type $Z$ in the Church +encoding. \subsubsection{Exercise \label{subsec:Exercise-free-type-6}\ref{subsec:Exercise-free-type-6}} -Assuming that $F^{\bullet}$ is a functor, define $Q^{A}\triangleq\exists Z.F^{Z}\times\left(Z\rightarrow A\right)$ +Assuming that $F$ is a functor, define $Q^{A}\triangleq\exists Z.F^{Z}\times\left(Z\rightarrow A\right)$ and implement \lstinline!f2q!$:F^{A}\rightarrow Q^{A}$ and \lstinline!q2f!$:Q^{A}\rightarrow F^{A}$. Show that these functions are natural transformations, and that they are inverses of each other \textsf{``}observationally\textsf{''}, i.e., after applying -\lstinline!q2f! in order to compare values of $Q^{A}$. +\lstinline!q2f! in order to compare values of type $Q^{A}$. \subsubsection{Exercise \label{subsec:Exercise-free-type-7}\ref{subsec:Exercise-free-type-7}} @@ -5031,11 +5031,10 @@ \section{Working with quantified types} {*}{*}{*}Move all this to an appendix? {*}{*}{*} also discuss existential types? -In the notation used in this book, there is a key difference between -the quantified type $\forall X.\,F^{X}$ and the type expression $F^{X}$ -that contains the type parameter $X$. In both cases, $X$ is a completely -unknown type parameter, and an example of Scala code implementing -those types would be: +There is an important difference between the quantified type $\forall X.\,F^{X}$ +and the type expression $F^{X}$ that contains the type parameter +$X$. In both cases, $X$ is a completely unknown type parameter, +and an example of Scala code implementing those types would be: \begin{lstlisting} def f[X]: F[X] = ??? \end{lstlisting} @@ -5056,33 +5055,28 @@ \subsection{The Yoneda identities for type constructors} \subsubsection{Statement \label{subsec:Statement-covariant-yoneda-identity-for-type-constructors}\ref{subsec:Statement-covariant-yoneda-identity-for-type-constructors} (covariant Yoneda identity for functors)\index{Yoneda identity!for functors}} -Assume that $P^{\bullet}$ is any type constructor and $S$ is a higher-order +Assume that $P$ is any type constructor and $S$ is a higher-order functor\index{functor!higher-order}\index{higher-order functor}. -That is, $S^{F^{\bullet}}$ is a type that depends covariantly on -an arbitrary type constructor $F^{\bullet}$. (An example of such -$S$ is $S^{F^{\bullet}}\triangleq F^{\text{Int}}\times F^{\text{String}}$.) -Then the type $S^{P^{\bullet}}$ is equivalent to the function type -$\forall F^{\bullet}.\,(P^{\bullet}\leadsto F^{\bullet})\rightarrow S^{F^{\bullet}}$, -where the function is required to be natural in the parameter $F^{\bullet}$. -The corresponding naturality law for functions $\sigma$ of type $\forall F^{\bullet}.\,(P^{\bullet}\leadsto F^{\bullet})\rightarrow S^{F}$ -involves arbitrary type constructors $Q^{\bullet}$, $R^{\bullet}$, -and arbitrary functions $f:P^{\bullet}\leadsto Q^{\bullet}$ and $g:Q^{\bullet}\leadsto R^{\bullet}$, -and may be written as +That is, $S^{F}$ is a type that depends covariantly on an arbitrary +type constructor $F$. (An example of such $S$ is $S^{F}\triangleq F^{\text{Int}}\times F^{\text{String}}$.) +Then the type $S^{P}$ is equivalent to the function type $\forall F.\,(P\leadsto F)\rightarrow S^{F}$, +where the function is required to be natural in the (type costructor) +parameter $F$. The corresponding naturality law for functions $\sigma$ +of type $\forall F.\,(P\leadsto F)\rightarrow S^{F}$ involves arbitrary +type constructors $Q$, $R$, and arbitrary functions $f:P\leadsto Q$ +and $g:Q\leadsto R$, and may be written as \begin{equation} \sigma^{Q}(f)\bef g^{\uparrow S}=\sigma^{R^{\bullet}}(f\bef g)\quad.\label{eq:assumed-naturality-of-argument-sigma} \end{equation} -Here, $g^{\uparrow S}$ has type $S^{Q^{\bullet}}\rightarrow S^{R^{\bullet}}$ -and is a lifting of the function $g:Q^{\bullet}\leadsto R^{\bullet}$ -to the higher-order functor $S$. At the same time, the functions -$f$ and $g$ do \emph{not} have to be natural transformations, and -the type constructors $F^{\bullet}$, $P^{\bullet}$, $Q^{\bullet}$, -$R^{\bullet}$ are \emph{not} required to be functors. +Here, $g^{\uparrow S}$ has type $S^{Q}\rightarrow S^{R}$ and is +a lifting of the function $g:Q\leadsto R$ to the higher-order functor +$S$. At the same time, the functions $f$ and $g$ do \emph{not} +have to be natural transformations, and the type constructors $F$, +$P$, $Q$, $R$ are \emph{not} required to be functors. \subparagraph{Proof} -{*}{*}{*}rewrite the proof in a simpler way like in chapter 10{*}{*}{*}For -brevity, we will write just $F$ and $P$ instead of $F^{\bullet}$ -and $P^{\bullet}$. +{*}{*}{*}rewrite the proof in a simpler way like in chapter 10{*}{*}{*} The isomorphism is implemented via two functions \lstinline!toC! and \lstinline!fromC!: @@ -5128,10 +5122,11 @@ \subsection{Recursive type equations with different fixpoints} A recursive type is defined as a \textbf{fixpoint} of\index{fixpoint of a functor} a functor. A fixpoint means a solution of a type equation\index{recursive type equation} -of the form $T\cong F^{T}$, where $F$ is a \textsf{``}structure functor\textsf{''} -that specifies the details of the type recursion. A solution of the -type equation $T\cong F^{T}$ is a type $T$ that is equivalent to -$F^{T}$ via two isomorphisms: +of the form $T\cong F^{T}$, where the type constructor $F$ is a +\textbf{recursion scheme}\index{recursion scheme} that specifies +how the type uses itself in its recursive definition. A solution of +the type equation $T\cong F^{T}$ is a type $T$ that is equivalent +to $F^{T}$ via two isomorphisms: \[ \text{fix}:F^{T}\rightarrow T\quad,\quad\quad\text{unfix}:T\rightarrow F^{T}\quad,\quad\quad\text{fix}\bef\text{unfix}=\text{id}\quad,\quad\text{unfix}\bef\text{fix}=\text{id}\quad. \] @@ -5267,9 +5262,10 @@ \subsubsection{Example \label{subsec:Example-fixpoint-list}\ref{subsec:Example-f \[ \text{List}^{A}\cong\bbnum 1+A\times\text{List}^{A}\quad. \] -We can write that equation in the form $\text{List}^{A}\cong F^{\text{List}^{A}}$ -where $F^{R}\triangleq\bbnum 1+A\times R$, and we consider $A$ to -be a fixed type. Consider a modified recursion scheme: +We can write that equation in the form $T\cong F^{T}$ where $F^{R}\triangleq\bbnum 1+A\times R$. +Considering $A$ to be a fixed type, we find $T=\text{List}^{A}$. + +Consider a modified recursion scheme: \[ G^{R}\triangleq P+A\times R\quad, \] @@ -5278,8 +5274,8 @@ \subsubsection{Example \label{subsec:Example-fixpoint-list}\ref{subsec:Example-f \subparagraph{Solution} -First, we will guess the answer. Intuitively, we imagine $\text{List}^{A}$ -to be an \textsf{``}infinite disjunction\textsf{''} of the form: +First, we will guess the answer. Intuitively, we imagine the type +$\text{List}^{A}$ to be an \textsf{``}infinite disjunction\textsf{''} of the form: \begin{align*} \text{List}^{A} & =\bbnum 1+A\times(\bbnum 1+A\times(\bbnum 1+...))\quad.\\ & =\bbnum 1+A+A\times A+A\times A\times A+... @@ -5359,8 +5355,7 @@ \subsubsection{Example \label{subsec:Example-fixpoint-list}\ref{subsec:Example-f \subsubsection{Example \label{subsec:Exampleexample-double-recursive-coproduct}\ref{subsec:Exampleexample-double-recursive-coproduct}} -Let $F^{\bullet,\bullet}$ be a bifunctor and define $P^{\bullet}$ -and $Q$ by: +Let $F$ be a bifunctor and define $P$ and $Q$ by: \[ P^{A}\cong F^{P^{A},A}\quad,\quad\quad Q\cong P^{Q}\quad. \] diff --git a/sofp-src/tex/sofp-functors.tex b/sofp-src/tex/sofp-functors.tex index da2c3b69b..337d8a200 100644 --- a/sofp-src/tex/sofp-functors.tex +++ b/sofp-src/tex/sofp-functors.tex @@ -23,10 +23,10 @@ \subsection{Motivation. Type constructors that wrap data} to manipulate the stored data. In functional programming, manipulating means applying functions to data. So, if an integer value $123$ is \textsf{``}wrapped\textsf{''}, we should be able somehow to apply a function such -as \lstinline!{x => x * 2}! and obtain a \textsf{``}wrapped\textsf{''} value $246$. +as \lstinline!{ x => x * 2 }! and obtain a \textsf{``}wrapped\textsf{''} value $246$. Let us look at some often used type constructors defined in the Scala -standard library, such as \lstinline!Seq!, \lstinline!Try!, and +standard library, such as \lstinline!List!, \lstinline!Try!, and \lstinline!Future!. We notice the common features: \begin{itemize} \item There are some methods for creating a data structure that wraps zero @@ -40,23 +40,23 @@ \subsection{Motivation. Type constructors that wrap data} them wrapped. For example, \lstinline!List(10, 20, 30).map(_ + 5)! evaluates to \lstinline!List(15, 25, 35)!. \end{itemize} -The types \lstinline!Seq!, \lstinline!Try!, and \lstinline!Future! +The types \lstinline!List!, \lstinline!Try!, and \lstinline!Future! express quite different kinds of wrapping. The data structure implementing -\lstinline!Seq[A]! can hold a variable number of values of type \lstinline!A!. -The data structure \lstinline!Try[A]! holds either a successfully -computed value of type \lstinline!A! or a failure. The data structure -\lstinline!Future[A]! implements a computation that has been scheduled -to run but may not have finished yet, and may compute a value of type -\lstinline!A! (or fail) at a later time. - -Since the \textsf{``}wrappers\textsf{''} \lstinline!Seq!, \lstinline!Try!, and \lstinline!Future! -are so different, the methods for creating and reading the wrapped -values have different type signatures for each wrapper. However, the -method \lstinline!map! is similar in all three examples. We can say -generally that the \lstinline!map! method will apply a given function -\lstinline!f: A => B! to all the data of type \lstinline!A! stored -inside the wrapper, putting new data (of type \lstinline!B!) into -a wrapper of the same type: +\lstinline!List[A]! can hold a variable number of values of type +\lstinline!A!. The data structure \lstinline!Try[A]! holds either +a successfully computed value of type \lstinline!A! or a failure. +The data structure \lstinline!Future[A]! implements a computation +that has been scheduled to run but may not have finished yet, and +may compute a value of type \lstinline!A! (or fail) at a later time. + +Since the \textsf{``}wrappers\textsf{''} \lstinline!List!, \lstinline!Try!, and +\lstinline!Future! are so different, the methods for creating and +reading the wrapped values have different type signatures for each +wrapper. However, the method \lstinline!map! is similar in all three +examples. We can say generally that the \lstinline!map! method will +apply a given function \lstinline!f: A => B! to all the data of type +\lstinline!A! stored inside the wrapper, putting new data (of type +\lstinline!B!) into a wrapper of the same type: \begin{lstlisting} val a = List(x,y,z).map(f) // Result is List(f(x), f(y), f(z)). val b = Try(x).map(f) // Result is Try(f(x)). @@ -70,7 +70,7 @@ \subsection{Motivation. Type constructors that wrap data} def map[A, B]: F[A] => (A => B) => F[B] \end{lstlisting} -We can see that \lstinline!Seq!, \lstinline!Try!, and \lstinline!Future! +We can see that \lstinline!List!, \lstinline!Try!, and \lstinline!Future! are \textsf{``}wrappers\textsf{''} because they have a suitable \lstinline!map! method. This chapter focuses on the properties of \lstinline!map! that are common to \emph{all} wrapper types. We will ignore all other features @@ -409,12 +409,11 @@ \subsection{Functors: definition and examples\label{subsec:Functors:-definition- & \text{map}_{L}(x^{:L^{A}})(f^{:A\rightarrow B})=\text{fmap}_{L}(f^{:A\rightarrow B})(x^{:L^{A}})\quad. \end{align*} -Each of the type constructors \lstinline!Option!, \lstinline!Seq!, -\lstinline!Try!, and \lstinline!Future! has its own definition of -\lstinline!map! but the functor laws remain the same. We use the -subscript $L$ when writing $\text{map}_{L}$ and $\text{fmap}_{L}$ -in order to indicate clearly the type constructor those functions -work with. +Each of the type constructors \lstinline!Option!, \lstinline!List!, +\lstinline!Try!, \lstinline!Future! has its own definition of \lstinline!map!, +but the functor laws remain the same. We use the subscript $L$ when +writing $\text{map}_{L}$ and $\text{fmap}_{L}$ in order to indicate +clearly the type constructor those functions work with. We will now look at some examples of type constructors that are functors. @@ -470,7 +469,7 @@ \subsection{Functors: definition and examples\label{subsec:Functors:-definition- } \end{lstlisting} This code defines both the type \lstinline!Counted! and the method -\lstinline!map!. Here is a usage example: +\lstinline!map!. Test this code: \begin{lstlisting} scala> Counted(10, "abc").map(s => "prefix " + s) res0: Counted[String] = Counted(10,prefix abc) @@ -753,10 +752,11 @@ \subsubsection{Example \label{subsec:Example-rec-poly-functor-List}\ref{subsec:E In this book, recursive calls to functions are indicated by an overline, as in $\overline{\text{fmap}}$. Keep in mind that the symbol $\overline{\text{fmap}}$ denotes \emph{the same} function as $\text{fmap}$. The overline is -used only as a reminder that certain parts of code are recursive calls. +used only as a reminder that certain parts of code are recursive calls +for which the inductive assumptions hold. -To verify the identity law, we apply $\text{fmap}$ to an identity -function ($\text{id}^{:A\rightarrow A}$) and find: +To verify the identity law, we apply \lstinline!fmap! to an identity +function ($\text{id}^{:A\rightarrow A}$): \[ \text{fmap}\,(\text{id})=\,\begin{array}{|c||cc|} & \bbnum 1 & A\times\text{List}^{A}\\ @@ -945,18 +945,21 @@ \subsection{Functor block expressions\index{functor block}} A confusing feature of the \lstinline!for! / \lstinline!yield! syntax is that, at first sight, this code: \begin{lstlisting} -for { x <- p; ... } yield expr(x) +val result = for { x <- List(1, 2, 3); y = x * x } yield y \end{lstlisting} -appears to compute the value \lstinline!expr(x)! because the code -says \lstinline!yield expr(x)!. However, this is not so. As the above -examples show, if \lstinline!p! is a sequence then the functor block -also computes a \emph{sequence}; that will be a sequence of values -of the form \lstinline!expr(x)! for various \lstinline!x!. In general, -the result of a functor block is a \textsf{``}wrapped\textsf{''} value, where the -type of the \textsf{``}wrapper\textsf{''} is determined by the first line of the functor -block. The first line must have a left arrow followed by a \textsf{``}source\index{functor block!source}\textsf{''}, +appears to return the value \lstinline!y! because the code says \textsf{``}\lstinline!yield y!\textsf{''}. +However, this is not so. The type of \lstinline!result! in this example +is \lstinline!List[Int]!, not \lstinline!Int!. The code: +\begin{lstlisting} +for { x <- p ; ... } yield f(x) +\end{lstlisting} +computes a \emph{sequence} of values of the form \lstinline!f(x)! +for various \lstinline!x!. In general, the result of a functor block +is a \textsf{``}wrapped\textsf{''} value, where the type of the \textsf{``}wrapper\textsf{''} is determined +by the first line of the functor block. The first line must have a +left arrow followed by a \textsf{``}source\index{functor block!source}\textsf{''}, which must be an expression of a functor type, i.e., of type \lstinline!L[A]! -for some functor \lstinline!L[_]!. The result\textsf{'}s type will be \lstinline!L[B]! +for some functor \lstinline!L!. The result\textsf{'}s type will be \lstinline!L[B]!, where \lstinline!B! is the type of the expression after the \lstinline!yield! keyword. @@ -1035,8 +1038,8 @@ \subsection{Functor block expressions\index{functor block}} \paragraph{Functor blocks and functor laws} -There is an important connection between the functor laws and the -properties of code in functor blocks. Consider this code: +There is a direct connection between the functor laws and the properties +of code in functor blocks. Consider this code: \begin{lstlisting} def f(x: Int) = x * x def g(x: Int) = x - 1 @@ -1048,7 +1051,7 @@ \subsection{Functor block expressions\index{functor block}} } yield g(z) res0: List[Int] = List(99, 399, 899) \end{lstlisting} -The code says that \lstinline!x = y!, so it appears reasonable to +The code says \textsf{``}\lstinline!y = x!\textsf{''}, so it appears reasonable to eliminate \lstinline!y! and simplify this code into: \begin{lstlisting} scala> for { @@ -1057,6 +1060,7 @@ \subsection{Functor block expressions\index{functor block}} } yield g(z) res1: List[Int] = List(99, 399, 899) \end{lstlisting} + Another example of a reasonable refactoring is to combine transformations: \begin{lstlisting} scala> for { @@ -1075,13 +1079,14 @@ \subsection{Functor block expressions\index{functor block}} } yield g(z) res3: List[Int] = List(120, 440, 960) \end{lstlisting} + Looking at these code changes, we expect that the computed results -will remain the same. Indeed, when the code directly states that \lstinline!x = y!, -it would be confusing and counter-intuitive if the result value changed -after replacing \lstinline!y! by \lstinline!x!. When the code says -that \lstinline!y = x + 1!, ordinary mathematical reasoning suggests -that \lstinline!f(y)! can be replaced by \lstinline!f(x + 1)! without -affecting the results. +will remain the same. Indeed, when the code defines that \textsf{``}\lstinline!y = x!\textsf{''}, +it would be confusing and counter-intuitive if the result values changed +after replacing \lstinline!y! by \lstinline!x! at some other place +in the code. When the code says that \lstinline!y = x + 1!, ordinary +mathematical reasoning suggests that \lstinline!f(y)! can be replaced +by \lstinline!f(x + 1)! anywhere without affecting the results. \begin{table} \begin{centering} @@ -1216,8 +1221,8 @@ \subsection{Functor block expressions\index{functor block}} of appropriate types. Functor laws guarantee that we can correctly understand and modify -code written in functor blocks, reasoning about transformations of -values as we do in mathematics. +code in functor blocks, reasoning about transformations of values +according to the standard logic of mathematics. \subsection{Examples of non-functors\label{subsec:Examples-of-non-functors}} @@ -1242,7 +1247,7 @@ \subsection{Examples of non-functors\label{subsec:Examples-of-non-functors}} \paragraph{Cannot implement \texttt{map}\textsf{'}s type signature} -Consider the type constructor $H$ defined by: +Consider this type constructor $H$: \begin{lstlisting} final case class H[A](r: A => Int) \end{lstlisting} @@ -1257,15 +1262,16 @@ \subsection{Examples of non-functors\label{subsec:Examples-of-non-functors}} \text{map}^{A,B}:\left(A\rightarrow\text{Int}\right)\rightarrow\left(A\rightarrow B\right)\rightarrow\left(B\rightarrow\text{Int}\right)\quad. \] To see this, recall that a \index{fully parametric!function}fully -parametric function needs to treat all types as type parameters. The -code: +parametric function needs to treat all types as type parameters. We +could satisfy the type signature of \lstinline!map! by this code: \begin{lstlisting} -def map[A, B]: H[A] => (A => B) => H[B] = { r => f => C(_ => 123) } +def map[A, B]: H[A] => (A => B) => H[B] = { r => f => H(_ => 123) } \end{lstlisting} -satisfies the type signature of \lstinline!map! but is not fully -parametric because it returns a specific value \lstinline!123! of -type \lstinline!Int!, which is not allowed. Replacing the type \lstinline!Int! -by a new type parameter $N$, we obtain the type signature: +But this code violates the identity law of \lstinline!map!. We can +also see that this code returns a specific value \lstinline!123! +of type \lstinline!Int!, which is not allowed in fully parametric +code. Replacing the type \lstinline!Int! by a new type parameter +$N$, we obtain the type signature: \[ \text{map}^{A,B,N}:\left(A\rightarrow N\right)\rightarrow\left(A\rightarrow B\right)\rightarrow B\rightarrow N\quad. \] @@ -1357,8 +1363,7 @@ \subsection{Examples of non-functors\label{subsec:Examples-of-non-functors}} Since this \lstinline!map! function is the only available fully parametric implementation of the required type signature, we conclude that $Q$ -is not a functor (we cannot implement \lstinline!map! that satisfies -the laws). +is not a functor. \paragraph{Mistakes in implementing \texttt{map}} @@ -1370,7 +1375,9 @@ \subsection{Examples of non-functors\label{subsec:Examples-of-non-functors}} \] Here is the Scala code corresponding to this code notation: \begin{lstlisting} -def map[A, B](p: (A, A))(f: A => B): (B, B) = p match { case (x, y) => (f(y), f(x)) } +def map[A, B](p: (A, A))(f: A => B): (B, B) = p match { + case (x, y) => (f(y), f(x)) +} \end{lstlisting} This code swaps the values in the pair \lstinline!(x, y)!; it fails to preserve information about the order of those values. The functor @@ -1381,7 +1388,7 @@ \subsection{Examples of non-functors\label{subsec:Examples-of-non-functors}} {\color{greenunder}\text{definition of }\text{id}:}\quad & =y\times x\neq x\times y\quad. \end{align*} We should not have swapped the order of values in the pair. The correct -implementation of \lstinline!map! is: +code for \lstinline!map! is: \[ \text{map}\triangleq x^{:A}\times y^{:A}\rightarrow f^{:A\rightarrow B}\rightarrow f(x)\times f(y)\quad. \] @@ -1389,7 +1396,7 @@ \subsection{Examples of non-functors\label{subsec:Examples-of-non-functors}} Example \ref{subsec:f-Example-A-A-A} shows the type constructor $\text{Vec}_{3}$ with an incorrect implementation of \lstinline!map! that reorders -some parts of a tuple and duplicates other parts. The correct implementation +some parts of a tuple and duplicates other parts. The correct code preserves the order of parts in a tuple and neither duplicates nor omits any data. @@ -1399,9 +1406,9 @@ \subsection{Examples of non-functors\label{subsec:Examples-of-non-functors}} def map_bad[A, B]: Option[A] => (A => B) => Option[B] = { _ => _ => None } \end{lstlisting} This function always returns \lstinline!None!, losing information -and violating the identity law. However, we have already seen that -\lstinline!Option! has a different implementation of \lstinline!map! -that satisfies the functor laws. +and violating the identity law. However, \lstinline!Option! has a +standard implementation of \lstinline!map! that satisfies the functor +laws. Similarly, one could define \lstinline!map! for the \lstinline!List! type constructor to always return an empty list: @@ -1496,14 +1503,14 @@ \subsection{Examples of non-functors\label{subsec:Examples-of-non-functors}} \begin{lstlisting} final case class OnlyA[A, B](eab: Either[A, B]) { override def equals(y: Any): Boolean = (eab, y) match { - case (Left(a1), OnlyA(Left(a2))) => a1 == a2 // Values Left(a1) and Left(a2) might be equal. - case _ => false // Never equal if not both `Left`. + case (Left(a1), OnlyA(Left(a2))) => a1 == a2 // We have OnlyA(Left(a1)) == OnlyA(Left(a2)) when a1 == a2. + case _ => false // Never equal in any other case. } } \end{lstlisting} This implementation of \lstinline!equals! is mathematically invalid: it violates the reflexivity law ($\forall x.\,x=x$) because values -of the form \lstinline!OnlyA! are never equal to each other: +of the form \lstinline!OnlyA(Right(...))! are not equal to themselves: \begin{lstlisting} scala> OnlyA(Right(0)) equals OnlyA(Right(0)) res2: Boolean = false @@ -1585,7 +1592,7 @@ \subsection{Contrafunctors\label{subsec:Contrafunctors}} \text{map}^{A,B}:\left(A\rightarrow\text{Int}\right)\rightarrow\left(A\rightarrow B\right)\rightarrow B\rightarrow\text{Int}\quad. \] However, it is possible to implement a function called \lstinline!contramap! -with a different type signature ,where the function type is $B\rightarrow A$ +with a different type signature, where the function type is $B\rightarrow A$ instead of $A\rightarrow B$: \[ \text{contramap}^{A,B}:\left(A\rightarrow\text{Int}\right)\rightarrow\left(B\rightarrow A\right)\rightarrow B\rightarrow\text{Int}\quad. @@ -1603,7 +1610,7 @@ \subsection{Contrafunctors\label{subsec:Contrafunctors}} we define \lstinline!cmap! as: \begin{align} & \text{cmap}^{A,B}:\left(B\rightarrow A\right)\rightarrow H^{A}\rightarrow H^{B}\quad,\nonumber \\ - & \text{cmap}\triangleq f^{:B\rightarrow A}\rightarrow h^{:A\rightarrow\text{Int}}\rightarrow\left(f\bef h\right)^{:B\rightarrow\text{Int}}\quad.\label{eq:f-example-1-contrafmap} + & \text{cmap}\triangleq f^{:B\rightarrow A}\rightarrow h^{:A\rightarrow\text{Int}}\rightarrow f\bef h\quad.\label{eq:f-example-1-contrafmap} \end{align} The type signature of \lstinline!cmap! has the form of a \textsf{``}reverse lifting\textsf{''}: functions of type \lstinline!B => A! are lifted into @@ -1708,10 +1715,10 @@ \subsubsection{Example \label{subsec:f-Example-contrafunctor}\ref{subsec:f-Examp while functors \textsf{``}wrap\textsf{''} data. By looking at the position of a given type parameter in a type expression such as $A\times\text{Int}$ or $A\rightarrow A\rightarrow\text{Int}$, we can see whether the type -parameter is \textsf{``}consumed\textsf{''} or \textsf{``}wrapped\textsf{''}: A type parameter to -the left of a function arrow is being \textsf{``}consumed\textsf{''}; a type parameter -to the right of a function arrow (or used without a function arrow) -is being \textsf{``}wrapped\textsf{''}. We will make this intuition precise in Section~\ref{sec:f-Laws-and-structure}. +parameter is \textsf{``}consumed\textsf{''} or \textsf{``}wrapped\textsf{''}. Type parameters to the +left of a function arrow are \textsf{``}consumed\textsf{''}; type parameters to the +right of a function arrow (or used without a function arrow) are \textsf{``}wrapped\textsf{''}. +We will make this intuition precise in Section~\ref{sec:f-Laws-and-structure}. \paragraph{Type constructors that are not contrafunctors } @@ -1735,25 +1742,25 @@ \subsubsection{Example \label{subsec:f-Example-contrafunctor}\ref{subsec:f-Examp \subsection{Subtyping, covariance, and contravariance\label{subsec:Covariance,-contravariance,-and-subtyping}} -Ordinarily, applying a function of type $Q\rightarrow R$ to a value -of a different type $P$ is an error: +Ordinarily, it is a type error to apply a function of type $Q\rightarrow R$ +to a value of a different type $P\text{\ensuremath{\neq Q}}$ : \begin{lstlisting} def h(q: Q): R = ??? val p: P = ??? h(p) // Type error: expected type Q but found P. \end{lstlisting} -However, the Scala compiler admits this kind of code when $P$ is +However, the Scala compiler accepts this kind of code when $P$ is a \textsf{``}subtype\textsf{''} of $Q$. Programming languages that support subtyping allow us to use a value -of type $P$ in any expression instead of a value of type $Q$, when -$P$ is a subtype of $Q$. Each programming language defines in some -way what are the possible subtypes of every given type. +of type $P$ instead of a value of type $Q$ in any expression, as +long as $P$ is a subtype of $Q$. Each programming language defines +in some way the subtypes of every given type. We may imagine that the language\textsf{'}s compiler automatically converts values of type $P$ into values of type $Q$ using a fixed, compiler-provided \textsf{``}conversion function\textsf{''} of type $P\rightarrow Q$. It is convenient -to \emph{define} subtyping through the existence of a conversion function: +to \emph{define} subtyping through the existence of conversion functions: \subsubsection{Definition \label{subsec:Definition-subtyping}\ref{subsec:Definition-subtyping} } @@ -1771,8 +1778,10 @@ \subsubsection{Definition \label{subsec:Definition-subtyping}\ref{subsec:Definit of $Q$ in any expression). Then we find: $c^{:P\rightarrow Q}\triangleq p^{:P}\rightarrow\text{id}^{:Q\rightarrow Q}(p)$. In most programming languages, the allowed subtype conversion functions -do not transform any data but only reassign the types. Let us look -at some examples of subtyping of that kind. +do not transform any data but only reassign the types. So, the compiler +will not need to insert any additional machine code. The subtype conversion +does not lead to any decrease in performance. Let us look at some +examples of subtyping of that kind. The first example uses disjunctive types. Consider this type definition: \begin{lstlisting} @@ -1796,7 +1805,7 @@ \subsubsection{Definition \label{subsec:Definition-subtyping}\ref{subsec:Definit \end{lstlisting} The implementations of these subtype conversions look like the code of identity functions except for the reassignment of types. In the -matrix code notation, we write:{\small{} +matrix code notation:{\small{} \begin{align*} f_{0} & \triangleq\,\begin{array}{|c||ccc|} & \text{Zero} & \text{One} & \text{Two}\\ @@ -1869,21 +1878,19 @@ \subsubsection{Definition \label{subsec:Definition-subtyping}\ref{subsec:Definit function \lstinline!f2! shown above to convert a value of the type \lstinline!Two! into a value of the type \lstinline!AtMostTwo!. Since the code of \lstinline!f2! is equivalent to an identity function, -this conversion does not change any data and only reassigns some types. -So, the compiler will not need to insert any additional machine code. -The subtype conversion does not lead to any decrease in performance. +this conversion does not change any data and only reassigns some types. The void type (\lstinline!Nothing!, denoted by $\bbnum 0$ in the type notation) is special:\index{void type} it is a subtype of \emph{every} type $A$. The reason is that the type $\bbnum 0$ has \emph{no} values, so the conversion function \lstinline!absurd! $:\bbnum 0\rightarrow A$ (shown in Example~\ref{subsec:ch-Example-type-identity-0-to-A}) -exists but never actually needs to be applied. The Scala compiler -recognizes this automatically. +never actually needs to be applied. The Scala compiler recognizes +this automatically. Let us now consider subtyping for type constructors. If a type constructor -$L^{A}$ is a functor, we can use its $\text{fmap}_{L}$ method to -lift a subtype conversion function $f:P\rightarrow Q$ into: +$L$ is a functor, we can use its $\text{fmap}_{L}$ method to lift +a subtype conversion function $f:P\rightarrow Q$ into: \[ \text{fmap}_{L}(f):L^{P}\rightarrow L^{Q}\quad, \] @@ -1894,16 +1901,16 @@ \subsubsection{Definition \label{subsec:Definition-subtyping}\ref{subsec:Definit because $\text{fmap}_{L}(\text{id})=\text{id}$ (the functor $L$\textsf{'}s identity law). -Since $\bbnum 0$ is a subtype of any type $A$, the type $F^{\bbnum 0}$ -is a subtype of $F^{A}$ for any functor $F$. +Since $\bbnum 0$ is a subtype of any type $A$, we have $F^{\bbnum 0}\lesssim F^{A}$ +for any functor $F$. -If a type constructor $H^{A}$ is a contrafunctor, a subtype conversion +If a type constructor $H$ is a contrafunctor, a subtype conversion function $f^{:P\rightarrow Q}$ is lifted to: \[ -\text{cmap}_{H}(f):H^{Q}\rightarrow H^{P}\quad, +\text{cmap}_{H}(f):H^{Q}\rightarrow H^{P}\quad. \] -showing that $H^{Q}$ is a subtype of $H^{P}$. The identity law of -the contrafunctor $H$, +So, $H^{Q}$ is a subtype of $H^{P}$. The identity law of the contrafunctor +$H$, \[ \text{cmap}_{H}(\text{id})=\text{id}\quad, \] @@ -1956,11 +1963,11 @@ \subsubsection{Definition \label{subsec:Definition-subtyping}\ref{subsec:Definit for \lstinline!Counted!. (This property holds also in programming languages that do not support subtyping.) The variance annotation \lstinline!Counted[+A]! merely tells the Scala compiler to activate -the automatic subtyping features for \lstinline!Counted!. Before -doing that, the Scala compiler will check that \lstinline!Counted! -is in fact covariant. It will be a type error if a variance annotation -specified by the programmer does not match the actual covariance or -contravariance of the type constructor: +the automatic subtyping features for \lstinline!Counted!. While doing +that, the Scala compiler will check that \lstinline!Counted! is in +fact covariant. It is a type error if a variance annotation specified +by the programmer does not match the actual covariance or contravariance +of the type constructor: \begin{lstlisting} final case class Counted[-A](n: Int, a: A) // Compile-time error. @@ -2254,7 +2261,9 @@ \subsubsection{Example \label{subsec:f-Example-functors-4}\ref{subsec:f-Example- {\color{greenunder}\text{nameless function}:}\quad & =f^{:A\rightarrow C}\rightarrow g^{:\left(A\rightarrow\text{Int}\right)\rightarrow Z}\rightarrow p^{:C\rightarrow\text{Int}}\rightarrow\gunderline{\text{???}^{:Z}}\\ {\color{greenunder}\text{apply }g\text{ to get a }Z:}\quad & =f^{:A\rightarrow C}\rightarrow g^{:\left(A\rightarrow\text{Int}\right)\rightarrow Z}\rightarrow p^{:C\rightarrow\text{Int}}\rightarrow g(\gunderline{\text{???}^{:A\rightarrow\text{Int}}})\\ {\color{greenunder}\text{nameless function}:}\quad & =f^{:A\rightarrow C}\rightarrow g^{:\left(A\rightarrow\text{Int}\right)\rightarrow Z}\rightarrow p^{:C\rightarrow\text{Int}}\rightarrow g(a^{:A}\rightarrow\gunderline{\text{???}^{:\text{Int}}})\\ -{\color{greenunder}\text{apply }p\text{ to get an Int}:}\quad & =f^{:A\rightarrow C}\rightarrow g^{:\left(A\rightarrow\text{Int}\right)\rightarrow Z}\rightarrow p^{:C\rightarrow\text{Int}}\rightarrow g(a^{:A}\rightarrow p(\gunderline{\text{???}^{:C}}))\\ +{\color{greenunder}\text{apply }p\text{ to get an Int}:}\quad & =f^{:A\rightarrow C}\rightarrow g^{:\left(A\rightarrow\text{Int}\right)\rightarrow Z}\rightarrow p^{:C\rightarrow\text{Int}}\rightarrow g(a^{:A}\rightarrow p(\gunderline{\text{???}^{:C}})) +\end{align*} +\begin{align*} {\color{greenunder}\text{apply }f\text{ to get a }C:}\quad & =f^{:A\rightarrow C}\rightarrow g^{:\left(A\rightarrow\text{Int}\right)\rightarrow Z}\rightarrow p^{:C\rightarrow\text{Int}}\rightarrow g(a^{:A}\rightarrow p(f(\gunderline{\text{???}^{:A}})))\\ {\color{greenunder}\text{use argument }a^{:A}:}\quad & =f\rightarrow g\rightarrow p\rightarrow g(a\rightarrow p(f(a))\quad. \end{align*} @@ -2279,7 +2288,7 @@ \subsubsection{Example \label{subsec:f-Example-functors-4}\ref{subsec:f-Example- The analysis is simpler for the type parameter $B$ because it is only used in covariant positions, never to the left of function arrows. So, we expect $\text{Data2}^{A,B}$ to be a functor with respect to -$B$. Implementing the corresponding \lstinline!fmap! is straightforward: +$B$. Writing the corresponding \lstinline!fmap! is straightforward: \begin{lstlisting} def fmapB[Z, B, C](f: B => C): Data2[Z, A] => Data2[Z, B] = { case (e: Either[Z, B], g: ((Z => Int) => B)) => @@ -2288,7 +2297,7 @@ \subsubsection{Example \label{subsec:f-Example-functors-4}\ref{subsec:f-Example- case Right(z) => Right(z) } val newG: (C => Int) => Z = { p => g(a => p(f(a))) } - (newE, newG) // This has type Data2[C, Z]. + (newE, newG) // This has type Data2[Z, B]. } \end{lstlisting} @@ -2349,7 +2358,7 @@ \subsubsection{Exercise \label{subsec:f-Exercise-functors}\ref{subsec:f-Exercise \begin{lstlisting} def fmap[A, B](f: A => B): Either[A, A] => Either[B, B] = { case Left(a) => Right(f(a)) - case Right(a: Int) => Left(f(a + 1)) + case Right(a: Int) => Right(f(a + 1)) case Right(a) => Left(f(a)) } \end{lstlisting} @@ -2391,8 +2400,8 @@ \section{Laws and structure\label{sec:f-Laws-and-structure}} A type constructor is a functor if it admits a lawful \lstinline!map! function. How can we recognize quickly whether a given type constructor -is a functor or a contrafunctor? For example, consider the type constructor -$Z^{A,R}$ defined by: +is a functor? For example, consider the type constructor $Z^{A,R}$ +defined by: \begin{equation} Z^{A,R}\triangleq\left(\left(A\rightarrow A\rightarrow R\right)\rightarrow R\right)\times A+\left(\bbnum 1+R\rightarrow A+\text{Int}\right)+A\times A\times\text{Int}\times\text{Int}\quad.\label{eq:f-example-complicated-z} \end{equation} @@ -2470,7 +2479,8 @@ \subsection{Functor laws in the pipe notation} \left(g\circ f\right)^{\uparrow L}=g^{\uparrow L}\circ f^{\uparrow L}\quad. \] -The lifting notation for a contrafunctor $C$ is: +The lifting notation for a contrafunctor $C$ uses a downward arrow +($^{\downarrow C}$): \[ f^{\downarrow C}\triangleq\text{cmap}_{C}(f)\quad. \] @@ -2499,8 +2509,12 @@ \subsection{Bifunctors\label{subsec:Bifunctors}} \] If we fix the type parameter $B$ in $F^{A,B}$ but let the parameter $A$ vary, we get a type constructor that we denote by $F^{\bullet,B}$. -We see that the type constructor $F^{\bullet,B}$ is a functor with -the following \lstinline!fmap! function: +The bullet symbol ($\bullet$) denotes the position of the free type +parameter. So, $F^{\bullet,B}$ is a type constructor with one type +parameter (and with $B$ fixed). + +The type constructor $F^{\bullet,B}$ is a functor with the following +\lstinline!fmap! function: \[ \text{fmap}_{F^{\bullet,B}}(f^{:A\rightarrow C})\triangleq a^{:A}\times b_{1}^{:B}\times b_{2}^{:B}\rightarrow f(a)\times b_{1}\times b_{2}\quad. \] @@ -2546,8 +2560,8 @@ \subsection{Bifunctors\label{subsec:Bifunctors}} } \] Different paths in this diagram give the same results if they arrive -at the same vertex (as mathematicians say, \textsf{``}the diagram is \index{commutative diagram}commutative\textsf{''}). -In this way, the diagram illustrates at once the commutativity law~(\ref{eq:f-fmap-fmap-bifunctor-commutativity}) +at the same vertex (as mathematicians say, \textsf{``}the diagram is \index{commutative diagram}\textbf{commutative}\textsf{''}). +This diagram illustrates at once the law~(\ref{eq:f-fmap-fmap-bifunctor-commutativity}) and the definition~(\ref{eq:f-definition-of-bimap}) of $\text{bimap}_{F}$. Let us verify the commutativity law for the bifunctor\index{commutativity law!of bifunctors} @@ -2681,8 +2695,8 @@ \subsection{Constructions of functors\label{subsec:f-Functor-constructions}} \subsubsection{Statement \label{subsec:f-Statement-identity-functor}\ref{subsec:f-Statement-identity-functor}} -The type constructor $\text{Id}^{A}\triangleq A$ is a lawful functor -(the \textbf{identity functor}\index{identity functor}). +The \textbf{identity functor}\index{identity functor} $\text{Id}^{A}\triangleq A$ +obeys the functor laws. \subparagraph{Proof} @@ -2737,7 +2751,8 @@ \subsubsection{Statement \label{subsec:f-Statement-constant-functor}\ref{subsec: \subsubsection{Statement \label{subsec:functor-Statement-functor-product}\ref{subsec:functor-Statement-functor-product}} If $L$ and $M$ are two functors then the product\index{functor product} -$P^{A}\triangleq L^{A}\times M^{A}$ is also a functor. +$P^{A}\triangleq L^{A}\times M^{A}$ is also a functor. We will write +$P=L\times M$ for brevity. \subparagraph{Proof} @@ -2849,7 +2864,7 @@ \subsubsection{Statement \label{subsec:functor-Statement-functor-coproduct}\ref{ \end{array}\quad. \] Here we assume that lawful \lstinline!fmap! functions are given for -the functors $P$ and $Q$. +the functors $P$ and $Q$. We will write $L=P+Q$ for brevity. \subparagraph{Proof} @@ -3016,7 +3031,7 @@ \subsubsection{Statement \label{subsec:functor-Statement-functor-exponential}\re The code \lstinline!p.map(_.map(f))! lifts an $f^{:A\rightarrow B}$ into a function of type \lstinline!List[Option[A]] => List[Option[B]]!. In this way, we may perform the \lstinline!map! operation on the -nested data type \lstinline!List[Option[A]]!. +composite data type \lstinline!List[Option[A]]!. The next statement shows that this code always produces a lawful \lstinline!map! function. In other words, the composition\index{functor composition} @@ -3080,9 +3095,9 @@ \subsubsection{Statement \label{subsec:functor-Statement-functor-composition-1}\ \begin{equation} L^{A}\triangleq S^{A,L^{A}}\quad,\label{eq:f-def-recursive-functor} \end{equation} -where $S^{A,R}$ is a suitably chosen type constructor (called a \index{recursion scheme|textit}\textbf{recursion -scheme}) with two type parameters $A,R$. If a recursion scheme $S$ -is given, the Scala code defining $L$ can be written as: +where $S$ is a suitably chosen bifunctor called a \index{recursion scheme|textit}\textbf{recursion +scheme}. If a recursion scheme $S$ is given, the Scala code defining +$L$ can be written as: \begin{lstlisting} type S[A, R] = ... // Must be defined as type alias, class, or trait. final case class L[A](x: S[A, L[A]]) // Define a recursive type L via a recursion scheme S. @@ -3352,7 +3367,7 @@ \subsubsection{Statement \label{subsec:functor-Statement-contrafunctor-compositi If we define a type constructor $L$ using the recursive \textsf{``}type equation\index{type equation}\textsf{''}: \[ -L^{A}\triangleq S^{A,L^{A}}\triangleq\left(A\rightarrow\text{Int}\right)+L^{A}\times L^{A}\quad, +L^{A}\triangleq S^{A,L^{A}}=\left(A\rightarrow\text{Int}\right)+L^{A}\times L^{A}\quad, \] we obtain a contrafunctor in the shape of a binary tree whose leaves are functions of type $A\rightarrow\text{Int}$. The next statement @@ -3433,8 +3448,8 @@ \subsection{Examples: Recognizing functors and contrafunctors\label{subsec:Solve position. For example, $F^{A}\triangleq\text{Int}\rightarrow A$ is covariant in $A$. \item Each time a type parameter is placed to the left of an \emph{uncurried} -function arrow $\rightarrow$, the variance is reversed: covariant -becomes contravariant and vice versa. For example: +function arrow, the variance is reversed: covariant becomes contravariant +and vice versa. For example: \begin{align*} {\color{greenunder}\text{this is covariant in }A:}\quad & \bbnum 1+A\times A\quad,\\ {\color{greenunder}\text{this is contravariant in }A:}\quad & \left(\bbnum 1+A\times A\right)\rightarrow\text{Int}\quad,\\ @@ -3453,11 +3468,10 @@ \subsection{Examples: Recognizing functors and contrafunctors\label{subsec:Solve is of the form $F^{A}\rightarrow\text{Int}$ with a type constructor $F^{A}\triangleq A\times A\times A$. Exercise~\ref{subsec:functor-Exercise-contrafunctor-exponential} will show that $F^{A}\rightarrow\text{Int}$ is contravariant in $A$. -\item Nested type constructors combine their variances: e.g., if $F^{A}$ -is contravariant in $A$ then $F^{A\rightarrow\text{Int}}$ is covariant -in $A$, while $F^{A\times A\times A}$ is contravariant in $A$. -This is shown in Exercises~\ref{subsec:functor-Exercise-functor-laws}(c), -(d). +\item Composition of type constructors will combine their variances: e.g., +if $F^{A}$ is contravariant in $A$ then $F^{A\rightarrow\text{Int}}$ +is covariant in $A$, while $F^{A\times A\times A}$ is contravariant +in $A$. See Exercise~\ref{subsec:functor-Exercise-functor-laws}(c),(d). \end{itemize} To see how this works in practice, consider any exponential-polynomial type expression, such as Eq.~(\ref{eq:f-example-complicated-z}): @@ -3475,17 +3489,16 @@ \subsection{Examples: Recognizing functors and contrafunctors\label{subsec:Solve we expect that $Z^{A,R}$ is a functor with respect to $A$, but not a functor (nor a contrafunctor) with respect to $R$. -To show that $Z^{A,R}$ is indeed a functor in the parameter $A$, -we need to implement a suitable \lstinline!map! method and verify -that the functor laws hold. To do that from scratch, we could use -the techniques explained in this and the previous chapters: starting -from the type signature, +To show that $Z^{A,R}$ is indeed covariant in $A$, we need to implement +a suitable \lstinline!map! method and verify that the functor laws +hold. To do that from scratch, we could use the techniques explained +in this and the previous chapters: starting from the type signature, \[ \text{map}_{Z}:Z^{A,R}\rightarrow\left(A\rightarrow B\right)\rightarrow Z^{B,R}\quad, \] we could derive a fully parametric, information-preserving implementation of \lstinline!map!. We could then look for proofs of the identity -and composition laws for that \lstinline!map! function. This would +and composition laws for that \lstinline!map! function. That would require a lot of work for a complicated type constructor such as $Z^{A,R}$. All that work can be avoided if we build $Z^{A,R}$ step by step via @@ -3499,7 +3512,7 @@ \subsection{Examples: Recognizing functors and contrafunctors\label{subsec:Solve \subsubsection{Example \label{subsec:f-Example-recognize-type-variance-1}\ref{subsec:f-Example-recognize-type-variance-1}\index{examples (with code)}} Rewrite the Scala definitions in the type notation. Are $F$ and $G$ -covariant or contravariant with respect to each type parameter? +covariant or contravariant with respect to their type parameters? \begin{lstlisting} final case class F[A, Z](f: Int => Z => (Int, A)) @@ -3615,10 +3628,11 @@ \subsubsection{Example \label{subsec:f-Example-recognize-type-variance-1-1}\ref{ G(newP, newQ) // The code of map for G_1. } \end{lstlisting} + In this way, the correct-by-construction code of $\text{fmap}_{G}$ may be \emph{derived} for any functor $G$ whose type expression is given, and similarly the code for $\text{cmap}_{C}$ for any given -contrafunctor $C$. The corresponding algorithms could be implemented +contrafunctor $C$. The corresponding algorithms could be even implemented as a Scala macro library that derives the code at compile time. \section{Summary} @@ -3634,35 +3648,62 @@ \section{Summary} \item Write more readable code using functor blocks to manipulate data wrapped in functors. \end{itemize} +What remains out of scope of those techniques? +\begin{itemize} +\item Class definitions in Scala may use mutable values (\lstinline!var!). +For example: +\begin{lstlisting} +case class Q[A](var a: A, counter: Int) +\end{lstlisting} +Because of the presence of a mutable value\index{mutable value}, +this type constructor is \emph{not} properly expressed by the type +formula $Q^{A}=A\times\text{Int}$. Mutable values (\lstinline!var!) +are neither in a covariant nor in a contravariant position. The functor +and contrafunctor constructions do \emph{not} apply to types that +contain \lstinline!var! definitions. +\item We have not talked about the type construction that imposes a universal +quantifier\index{universal quantifier} on a type parameter. For example, +if $S$ is a bifunctor then we may define a functor $F$ by $F^{A}\triangleq\forall B.\,S^{A,B}$. +The corresponding code in Scala 2 is: +\begin{lstlisting} +// The bifunctor S[_, _] should be already defined. +trait F[A]{ def s[B]: S[A, B] } +\end{lstlisting} +The syntax in Scala 3 is shorter: +\begin{lstlisting} +type F[A] = [B] => () => S[A, B] +\end{lstlisting} +This advanced construction is used less often. We will develop tools +for working with this construction in later chapters of this book. +\end{itemize} \subsection{Exercises: Functor and contrafunctor constructions \index{exercises}} \subsubsection{Exercise \label{subsec:functor-Exercise-contrafunctor-exponential}\ref{subsec:functor-Exercise-contrafunctor-exponential}} -If $H^{A}$ is a contrafunctor and $L^{A}$ is a functor, show that -$C\triangleq L^{A}\rightarrow H^{A}$ is a contrafunctor with the -\lstinline!cmap! method defined by the following code: +If $H$ is a contrafunctor and $L$ is a functor, show that $C^{A}\triangleq L^{A}\rightarrow H^{A}$ +is a contrafunctor with the \lstinline!cmap! method defined by the +following code: \begin{lstlisting}[mathescape=true] -def cmap[A, B](f: B => A)(c: L[A] => H[A]): L[B] => H[B] = { - lb: L[B] => cmap_H(f)(c(fmap_L(f)(lb))) // Code notation: ${\color{dkgreen}\scriptstyle f^{\downarrow C}\,\,\triangleq\, c\rightarrow f^{\uparrow L}\bef c\bef f^{\downarrow H}}$ -} +def cmap_C[A, B](f: B => A)(c: L[A] => H[A]): L[B] => H[B] = + (lb: L[B]) => cmap_H(f)(c(fmap_L(f)(lb))) // Code notation: ${\color{dkgreen}\scriptstyle f^{\downarrow C}\,\,\triangleq\, c\rightarrow f^{\uparrow L}\bef c\bef f^{\downarrow H}}$ \end{lstlisting} Here, \lstinline!cmap_H! and \lstinline!fmap_L! are the methods -already defined for $H$ and $L$. Prove that the laws hold. +already defined for $H$ and $L$. Prove that the laws hold for \lstinline!cmap_C!. \subsubsection{Exercise \label{subsec:functor-Exercise-functor-laws}\ref{subsec:functor-Exercise-functor-laws}} -Implement the required \lstinline!fmap! or \lstinline!cmap! function +Implement the \lstinline!fmap! or the \lstinline!cmap! function for the given type constructors $L$ and prove that the appropriate laws hold. Write the implementations both in Scala and in the code notation. Assume that the given type constructors $F$ and $G$ already satisfy their respective laws. \textbf{(a)} $L^{A}\triangleq F^{A}\times G^{A}$ is a contrafunctor -if $F^{A}$ and $G^{A}$ are contrafunctors. +if $F$ and $G$ are contrafunctors. \textbf{(b)} $L^{A}\triangleq F^{A}+G^{A}$ is a contrafunctor if -$F^{A}$ and $G^{A}$ are contrafunctors. +$F$ and $G$ are contrafunctors. \textbf{(c)} $L^{A}\triangleq F^{G^{A}}$ is a functor when both $F$ and $G$ are contrafunctors. @@ -3730,8 +3771,10 @@ \subsubsection{Exercise \label{subsec:functor-Exercise-functor-lifted-equivalenc \textbf{(a)} Given any functor $F$, show that if two types $A$ and $B$ are equivalent then the types $F^{A}$ and $F^{B}$ are also -equivalent. \textbf{(b)} Show that the same property holds for any -contrafunctor $F$. +equivalent. + +\textbf{(b)} Show that the same property holds for any contrafunctor +$F$. \section{Further developments} @@ -3756,7 +3799,7 @@ \subsection{Profunctors\label{subsec:f-Profunctors}} \[ \tilde{P}^{Z,A}\triangleq A+\left(Z\rightarrow\text{Int}\right)\quad. \] -The original type constructor $P^{A}$ is expressed as $P^{A}=\tilde{P}^{A,A}$. +The original type constructor $P$ is expressed as $P^{A}=\tilde{P}^{A,A}$. Now, $\tilde{P}^{Z,A}$ is covariant in $A$ and contravariant in $Z$. We can implement \lstinline!xmap!$_{\tilde{P}}$ as a composition of \lstinline!fmap! with respect to $A$ and \lstinline!cmap! with @@ -3773,17 +3816,16 @@ \subsection{Profunctors\label{subsec:f-Profunctors}} & \quad=\text{xmap}_{P}(f_{2}\bef f_{1})(g_{1}\bef g_{2})\quad. \end{align*} -A type constructor $\tilde{P}^{Z,A}$ with a lawful \lstinline!xmap!$_{\tilde{P}}$ -is called a profunctor\index{profunctor}. We will sometimes also -call the type constructor $P^{A}\triangleq\tilde{P}^{A,A}$ a profunctor. +This type constructor $\tilde{P}$ with a lawful \lstinline!xmap!$_{\tilde{P}}$ +is called a \textbf{profunctor}\index{profunctor}. We will also call +the type constructor $P^{A}\triangleq\tilde{P}^{A,A}$ a profunctor. -Consider an exponential-polynomial type constructor $P^{A}$ such -as: +Consider an exponential-polynomial type constructor $P$ such as: \[ P^{A}\triangleq\left(\bbnum 1+A\times A\rightarrow A\right)\times A\rightarrow\bbnum 1+\left(A\rightarrow A+\text{Int}\right)\quad. \] Each copy of the type parameter $A$ will occur either in covariant -or in a contravariant position because no other possibility is available +or in a contravariant position, because no other possibility is available in exponential-polynomial types. So, we can always rename all contravariant occurrences of the type parameter $A$ to $Z$ and obtain a new type constructor $\tilde{P}^{Z,A}$, which will be covariant in $A$ and @@ -3794,11 +3836,11 @@ \subsection{Profunctors\label{subsec:f-Profunctors}} which makes $P$ a profunctor. So, \emph{every} exponential-polynomial type constructor is a profunctor. -GADTs\index{GADT}, such as the disjunctive type \lstinline!ServerAction[R]! +GADTs\index{GADT}, such as the disjunctive type \lstinline!ServerAction! shown in Section~\ref{subsec:Examples-of-non-functors}, cannot be made into profunctors. The type signature of \lstinline!xmap! cannot -be implemented for \lstinline!ServerAction[R]! because it is not -a fully parametric type constructor (and so is not exponential-polynomial). +be implemented for \lstinline!ServerAction! because it is not a fully +parametric type constructor (and it is not exponential-polynomial). Profunctors are not often used in practical coding. We will see profunctors occasionally in later chapters where we need to reason about type @@ -3878,7 +3920,7 @@ \subsection{Subtyping with injective or surjective conversion functions\label{su \subsubsection{Statement \label{subsec:f-Statement-functor-preserves-injective}\ref{subsec:f-Statement-functor-preserves-injective}} -If $L^{A}$ is a lawful functor and $f^{:A\rightarrow B}$ is an injective +If $L$ is a lawful functor and $f^{:A\rightarrow B}$ is an injective function then $\text{fmap}_{L}(f)$ is also an injective function of type $L^{A}\rightarrow L^{B}$. If $f$ is surjective then the function $\text{fmap}_{L}(f)$ is also surjective.\index{subtyping!injective or surjective} diff --git a/sofp-src/tex/sofp-induction.tex b/sofp-src/tex/sofp-induction.tex index b5016b0bd..e0fed7e77 100644 --- a/sofp-src/tex/sofp-induction.tex +++ b/sofp-src/tex/sofp-induction.tex @@ -3767,7 +3767,7 @@ \subsection{Lazy values and sequences. Iterators and streams\label{subsec:Lazy-v It is surprising and counter-intuitive that a variable (here, \lstinline!s!) cannot be used twice; we expect that the code \lstinline!s.zip(s)! would just \textsf{``}zip\textsf{''} a given sequence \lstinline!s! with itself. -But Scala\textsf{'}s \lstinline!Iterator! class is \textbf{mutable}\index{mutability}: +But Scala\textsf{'}s \lstinline!Iterator! class is \textbf{mutable}\index{mutable value}: it gets modified during its use. This breaks the value-based reasoning about code.\index{Scala\textsf{'}s Iterator class@Scala\textsf{'}s \texttt{Iterator} class} diff --git a/sofp-src/tex/sofp-monads.tex b/sofp-src/tex/sofp-monads.tex index f78b0f69a..6dba0c77a 100644 --- a/sofp-src/tex/sofp-monads.tex +++ b/sofp-src/tex/sofp-monads.tex @@ -291,18 +291,20 @@ \subsubsection{Example \label{subsec:Example-list-monads-1}\ref{subsec:Example-l res0: Seq[String] = List(aaa, aab, aac, aba, abb, abc, aca, acb, acc, baa, bab, bac, bba, bbb, bbc, bca, bcb, bcc, caa, cab, cac, cba, cbb, cbc, cca, ccb, ccc) \end{lstlisting} To obtain all permutations and nothing else, we need to exclude all -repeated subsequences such as \lstinline!"aab"!. To achieve that, -we must make \lstinline!y! iterate over letters that do not include -the current value of \lstinline!x!: +repeated subsequences such as \lstinline!"aab"!. We use the inline +\textsf{``}\lstinline!if!\textsf{''} syntax to let \lstinline!y! iterate over letters +different from the previously chosen values of \lstinline!x!, and +to let \lstinline!z! iterate over letters different from the previously +chosen \lstinline!x! and \lstinline!y!: \begin{lstlisting} -val xs = Seq("a", "b", "c") +val letters = Seq("a", "b", "c") scala> for { - x <- xs - xsWithoutX = xs.filter(_ != x) - y <- xsWithoutX - xsWithoutXY = xsWithoutX.filter(_ != y) - z <- xsWithoutXY + x <- letters + y <- letters + if y != x + z <- letters + if z != x && z != y } yield x + y + z res1: Seq[String] = List(abc, acb, bac, bca, cab, cba) \end{lstlisting} @@ -4971,7 +4973,10 @@ \subsection{From semi-monads to monads: The identity laws} abstract class Monad[F[_]: Functor : Semi-monad] { def pure[A](a: A): F[A] } -def checkMonadIdentityLaws[F[_], A, B]()(implicit mf: Monad[F], sf: Semi-monad[F], +object Monad { // For convenience: enables Monad[F].pure() syntax. + def apply[F[_]: Monad]: Monad[F] = implicitly[Monad[F]] +} +def checkMonadIdentityLaws[F[_], A, B]()(implicit mf: Monad[F], sf: Semimonad[F], aa: Arbitrary[A], af: Arbitrary[F[A]], ab: Arbitrary[A => F[B]]) = { forAll { (x: A, g: A => F[B]) => mf.pure(x).flatMap(g) shouldEqual g(x) // Left identity law. @@ -6026,9 +6031,9 @@ \subsubsection{Statement \label{subsec:Statement-monad-construction-1}\ref{subse As with other monads of function type, derivations are made shorter by using the flipped Kleisli trick. We assume that $F$\textsf{'}s Kleisli composition $\diamond_{_{F}}$ is known and obeys the associativity -law. When $F$ is a monad, we also assume that its pure method $\text{pu}_{F}$ -is known and obeys the identity laws. For the monad $L$, we will -now define the \index{flipped@\textsf{``}flipped Kleisli\textsf{''} technique}flipped +law. When $F$ is a monad, we also assume that its \lstinline!pure! +method ($\text{pu}_{F}$) is known and obeys the identity laws. For +the monad $L$, we will now define the \index{flipped@\textsf{``}flipped Kleisli\textsf{''} technique}flipped Kleisli composition $\tilde{\diamond}_{_{L}}$ and prove its laws. The ordinary Kleisli functions have types $A\rightarrow Z\rightarrow F^{B}$, @@ -6071,7 +6076,7 @@ \subsubsection{Statement \label{subsec:Statement-monad-construction-1}\ref{subse \begin{lstlisting} type L[A] = Z => F[A] // A type Z and a semi-monad F must be already defined. def flatMap_L[A, B](la: L[A])(f: A => L[B]): L[B] = { z => la(z).flatMap(a => f(a)(z)) } -def pure_L[A](a: A): L[A] = { _ => implicitly[Monad[F]].pure(a) } +def pure_L[A](a: A): L[A] = { _ => Monad[F].pure(a) } \end{lstlisting} @@ -6632,21 +6637,23 @@ \subsubsection{Exercise \label{subsec:Exercise-1-monads-3-2}\ref{subsec:Exercise \subsubsection{Exercise \label{subsec:Exercise-1-monads-3}\ref{subsec:Exercise-1-monads-3}} The naturality law~(\ref{eq:naturality-law-of-flatten}) of \lstinline!flatten! -is written in the code notation as:\index{naturality law!of flatten@of \lstinline!flatten!} +is written as:\index{naturality law!of flatten@of \lstinline!flatten!} \[ f^{\uparrow S\uparrow S}\bef\text{ftn}_{S}=\text{ftn}_{S}\bef f^{\uparrow S}\quad. \] Show that one \emph{cannot} replace $f^{\uparrow S}$ in that law -by an arbitrary function $g:S^{A}\rightarrow S^{B}$. Find a counterexample +by an arbitrary function $g:S^{A}\rightarrow S^{B}$. (Find a counterexample with a specific $S$, $A$, $B$, and $g:S^{A}\rightarrow S^{B}$ -for which $g^{\uparrow S}\bef\text{ftn}_{S}\neq\text{ftn}_{S}\bef g$. +for which $g^{\uparrow S}\bef\text{ftn}_{S}\neq\text{ftn}_{S}\bef g$.) \subsubsection{Exercise \label{subsec:Exercise-1-monads-3-1}\ref{subsec:Exercise-1-monads-3-1}} Show that the laws hold for a non-standard \lstinline!List! monad -whose \lstinline!flatten! method is:\index{monads!List monad with empty sub-lists@\texttt{List} monad with empty sub-lists} +whose \lstinline!pure! method remains unchanged but the \lstinline!flatten! +method is:\index{monads!List monad with empty sub-lists@\texttt{List} monad with empty sub-lists} \begin{lstlisting} -def flatten[A](p: List[List[A]]): List[A] = if (p.exists(_.isEmpty)) Nil else p.flatten +def flatten[A](p: List[List[A]]): List[A] = + if (p.exists(_.isEmpty)) Nil else p.flatten \end{lstlisting} @@ -6662,13 +6669,12 @@ \subsubsection{Exercise \label{subsec:Exercise-1-monads-8-1}\ref{subsec:Exercise \subsubsection{Exercise \label{subsec:Exercise-1-monads-4}\ref{subsec:Exercise-1-monads-4}} Given an arbitrary monad $M$, show that the functor $F^{A}\triangleq\bbnum 2\times M^{A}$ -(which is equivalent to $M^{A}+M^{A}$) is a semi-monad but not a -full monad. +(which is equivalent to $M^{A}+M^{A}$) is a semi-monad but not always +a monad. \subsubsection{Exercise \label{subsec:Exercise-1-monads-9}\ref{subsec:Exercise-1-monads-9}} -Show that $P^{A}\triangleq Z+W\times A$ is a (full) monad if $W$ -is a monoid. +Show that $P^{A}\triangleq Z+W\times A$ is a monad if $W$ is a monoid. \subsubsection{Exercise \label{subsec:Exercise-1-monads-5}\ref{subsec:Exercise-1-monads-5}} @@ -6808,14 +6814,15 @@ \subsubsection{Exercise \label{subsec:Exercise-1-monads-16}\ref{subsec:Exercise- \textbf{(b)} A polynomial functor $F^{A}\triangleq p(A)$ when $p(x)$ is a polynomial of the form $p(x)=x^{n_{1}}+x^{n_{2}}+...+x^{n_{k}}$ -with some distinct positive integers $n_{1}<... 123 + x + x } \end{lstlisting} - Code notation: \[ x^{:\text{Int}}\rightarrow123+x+x\quad. \] If the expression \lstinline!expr! already contains \lstinline!x! -as a bound variable, the function \lstinline!{ x => expr }! will -have a name clash. An example is \lstinline!x => { x => x }!; here -\lstinline!expr! is \lstinline!{x => x}!. Such code is confusing. +as a \emph{bound} variable, the function \lstinline!{ x => expr }! +will have a name clash. An example is \lstinline!x => { x => x }!; +here \lstinline!expr! is \lstinline!{x => x}!. Such code is confusing. It is helpful to avoid the name clash by renaming the bound variables -inside \lstinline!expr!, e.g., to \lstinline!{ z => z }!. The resulting -code is written in Scala as: +inside \lstinline!expr!. In this example, let us rename \lstinline!{x => x}! +to \lstinline!{z => z}!. The resulting code is written in Scala as: \begin{lstlisting} { x: Int => { z: Int => z } } // Or equivalently: (x: Int) => (z: Int) => z @@ -206,17 +205,17 @@ \subsection{The nine constructions of fully parametric code} val s: S = P(10, 20) // Create a value of type S. val t: S = R(30) // Another value of type S. \end{lstlisting} -Code notation: +Code notation (with all types annotated for clarity): \begin{align*} S & \triangleq\text{Int}\times\text{Int}+\text{String}+\text{Int}\quad,\\ -s^{:S} & \triangleq10\times20+\bbnum 0^{:\text{String}}+\bbnum 0^{:\text{Int}}\quad,\\ -t^{:S} & \triangleq\bbnum 0^{:\text{Int}\times\text{Int}}+\bbnum 0^{:\text{String}}+30\quad. +s^{:S} & \triangleq(10\times20)^{:\text{Int}\times\text{Int}}+\bbnum 0^{:\text{String}}+\bbnum 0^{:\text{Int}}\quad,\\ +t^{:S} & \triangleq\bbnum 0^{:\text{Int}\times\text{Int}}+\bbnum 0^{:\text{String}}+30^{:\text{Int}}\quad. \end{align*} The code notation for disjunctive values, e.g., $\bbnum 0+\bbnum 0+x$, is more verbose than the Scala syntax such as \lstinline!R(x)!. The advantage is that we may explicitly annotate all types and show clearly -the part of the disjunction that we are creating. Another advantage +the part of the disjunctive type we are creating. Another advantage is that the notation $\bbnum 0+\bbnum 0+x$ is similar to a row vector, $\,\begin{array}{|ccc|} \bbnum 0 & \bbnum 0 & x\end{array}$~, which is well adapted to the matrix notation for \textsf{``}disjunctive @@ -314,10 +313,11 @@ \subsection{The nine constructions of fully parametric code} \] The void type\index{void type!in matrix notation} ($\bbnum 0$) is written in the first column to indicate that the disjunctive part -in that column is not returned. There is no confusion with other columns -because the type $\bbnum 0$ has no values. In this way, the matrix -clearly displays the parts of disjunctive types that are being returned -in each case. +($\bbnum 1+\bbnum 0^{:\text{Int}}$) corresponding to that column +is not returned. There is no confusion in using the symbol $\bbnum 0$ +because the void type $\bbnum 0$ has no values. The matrix clearly +displays the parts of disjunctive types that are being returned in +each case. Because only one part of a disjunctive type can ever be returned, a row can have at most one non-void value. That value will be in the @@ -370,7 +370,7 @@ \subsection{The nine constructions of fully parametric code} them from function matrices. Calculations use the standard rules of a vector-matrix product: \begin{align*} - & (\bbnum 0+64)\triangleright\,\begin{array}{||cc|} + & (\bbnum 0^{:\bbnum 1}+64^{:\text{Int}})\triangleright\,\begin{array}{||cc|} \bbnum 0 & \_\rightarrow100\\ \bbnum 0 & x\rightarrow\frac{x}{2} \end{array}\,=\,\begin{array}{|cc|} @@ -455,7 +455,7 @@ \subsection{Function composition and the pipe notation} right identity law as $f\bef\text{id}=f$ instead of $\text{id}\left(f(x)\right)=f(x)$. This is known as calculating in \index{point-free style|textit}\textbf{point-free} style (meaning \textsf{``}argument-free\textsf{''}). Many laws can be formulated and -used more easily in the point-free form. +used more easily in that style. Calculations in point-free style almost always involve composing functions. This book prefers to use the \emph{forward} function composition $(f\bef g$) @@ -511,7 +511,7 @@ \subsection{Function composition and the pipe notation} one rule that says how arguments are symbolically substituted as parameters into functions, for example: \begin{align*} -{\color{greenunder}\text{substitute }x\text{ instead of }a:}\quad & \gunderline x\triangleright(\gunderline a\rightarrow f(\gunderline a))=f(x)\quad.\\ +{\color{greenunder}\text{substitute }x\text{ instead of }a:}\quad & \gunderline x\triangleright(\gunderline a\rightarrow f(\gunderline a))=f(x)\quad,\\ {\color{greenunder}\text{substitute }f(x)\text{ instead of }y:}\quad & (x\rightarrow\gunderline{f(x)})\bef(\gunderline y\rightarrow g(\gunderline y))=x\rightarrow g(f(x))\quad. \end{align*} Whenever there is a doubt (is $x\triangleright(f\triangleright g)$ @@ -962,9 +962,9 @@ \subsection{Working with disjunctive types in matrix notation\label{subsec:Worki matrix. For instance, if we are calculating with an arbitrary function $f^{:\bbnum 1+A\rightarrow\bbnum 1+B}$, we cannot write that function in a form of a $2\times2$ matrix because we do not know which parts -of the disjunction are returned (the code of the function $f$ is -arbitrary and unknown). At most, we could split the \emph{rows} by -expressing the function $f$ through two unknown functions $g^{:\bbnum 1\rightarrow\bbnum 1+B}$ +of the disjunctive type are returned (the code of the function $f$ +is arbitrary and unknown). At most, we could split the \emph{rows} +by expressing the function $f$ through two unknown functions $g^{:\bbnum 1\rightarrow\bbnum 1+B}$ and $h^{:A\rightarrow\bbnum 1+B}$: \[ f=\,\begin{array}{|c||c|} @@ -1071,8 +1071,8 @@ \subsection{Working with disjunctive types in matrix notation\label{subsec:Worki \end{array}\quad. \] The rules of matrix multiplication do not help in deriving this law -directly. So, we use a more basic approach: show that both sides are -equal when applied to arbitrary values $p$ of type $A+B$: +directly. So, we use a more basic approach and show that both sides +are equal when applied to arbitrary values $p$ of type $A+B$: \[ p^{:A+B}\triangleright\Delta\bef(f\boxtimes g)=f(p)\times g(p)\overset{?}{=}p\triangleright\,\begin{array}{|c||c|} & R\times S\\ @@ -1140,8 +1140,7 @@ \subsection{Derivations involving unknown functions with laws} \end{align*} At this point, the only things we can simplify are the identity functions applied to arguments. We know that $F$ is a lawful functor; therefore, -$\text{id}^{\uparrow F}=\text{id}$. So, we continue the derivation, -omitting types: +$\text{id}^{\uparrow F}=\text{id}$. So, we continue the derivation: \begin{align*} {\color{greenunder}\text{expect to equal }\text{id}:}\quad & \text{id}^{\uparrow L}=a\times p\rightarrow\gunderline{\text{id}\,(a)}\times(p\triangleright\gunderline{\text{id}^{\uparrow F}})\\ {\color{greenunder}\text{identity law of }F:}\quad & =a\times p\rightarrow a\times(\gunderline{p\triangleright\text{id}})\\ diff --git a/sofp-src/tex/sofp-summary.tex b/sofp-src/tex/sofp-summary.tex index 5e32d268c..172ebc691 100644 --- a/sofp-src/tex/sofp-summary.tex +++ b/sofp-src/tex/sofp-summary.tex @@ -260,22 +260,8 @@ \subsection{Exercises\index{exercises}} \subsubsection{Exercise \label{par:Exercise-additional}\ref{par:Exercise-additional}} -Find the smallest integer expressible as a sum of two cubed integers -in more than one way. - -\subsubsection{Exercise \label{par:Exercise-additional-18}\ref{par:Exercise-additional-18}} - -Show that the following type signatures have \emph{no} fully parametric -implementations: - -\begin{lstlisting} -def f[A]: Option[A] => A -def g[A, B]: (A => B) => A -def h[A, B]: (A => B) => (B => A) -def k[A, B, C]: (A => B) => (B => C) => (C => A) -\end{lstlisting} - -Hint: set some type parameters to the void type (\lstinline!Nothing!). +Write a function for finding the smallest integer expressible as a +sum of two cubed integers in more than one way. \subsubsection{Exercise \label{par:Exercise-additional-1}\ref{par:Exercise-additional-1}} @@ -306,9 +292,8 @@ \subsubsection{Exercise \label{par:Exercise-additional-2}\ref{par:Exercise-addit \end{comment} -\subsubsection{Exercise \label{par:Exercise-additional-3}\ref{par:Exercise-additional-3}} +\subsubsection{Exercise \label{par:Exercise-additional-3}\ref{par:Exercise-additional-3}\protect\footnote{Exercise 4-1 from Hu Zhenjiang\textsf{'}s course \texttt{\protect\href{http://www.prg.nii.ac.jp/course/2015/msp15/}{http://www.prg.nii.ac.jp/course/2015/msp15/}}.}} -(Exercise 4-1 from Hu Zhenjiang\textsf{'}s course \texttt{\href{http://www.prg.nii.ac.jp/course/2015/msp15/}{http://www.prg.nii.ac.jp/course/2015/msp15/}}) Express the \lstinline!filter! method for sequences via \lstinline!flatMap!: \begin{lstlisting} def filter[A](p: A => Boolean)(s: Seq[A]): Seq[A] = s.flatMap { a => ??? } @@ -330,16 +315,15 @@ \subsubsection{Exercise \label{par:Exercise-additional-5}\ref{par:Exercise-addit \end{lstlisting} -\subsubsection{Exercise \label{par:Exercise-additional-6-1-1}\ref{par:Exercise-additional-6-1-1}} +\subsubsection{Exercise \label{par:Exercise-additional-6-1-1}\ref{par:Exercise-additional-6-1-1}\protect\footnote{This was posted in \texttt{\protect\href{https://cstheory.stackexchange.com/questions/53294}{https://cstheory.stackexchange.com/questions/53294}}}} -Find\footnote{This was posted in \texttt{\href{https://cstheory.stackexchange.com/questions/53294}{https://cstheory.stackexchange.com/questions/53294}}} -a general type signature for the expression $a\rightarrow a(y\rightarrow t\rightarrow t)(z(a))$. +Find a general type signature for the expression $a\rightarrow a(y\rightarrow t\rightarrow t)(z(a))$. -\subsubsection{Exercise \label{par:Exercise-additional-4}\ref{par:Exercise-additional-4}} +\subsubsection{Exercise \label{par:Exercise-additional-4}\ref{par:Exercise-additional-4}\protect\footnote{R.~Bird and O.~de Moor, \emph{Algebra of programming} (1996), page +20.}} -(Bird-de Moor, page 20) Derive the following identity between functions -$F^{A}\rightarrow F^{A}$, for any filterable functor $F$ and any -predicate $p^{:A\rightarrow\bbnum 2}$: +Derive the following identity between functions $F^{A}\rightarrow F^{A}$, +for any filterable functor $F$ and any predicate $p^{:A\rightarrow\bbnum 2}$: \[ \text{filt}_{F}(p)=(\Delta\bef\text{id}\boxtimes p)^{\uparrow F}\bef\text{filt}_{F}(\pi_{2})\bef\pi_{1}^{\uparrow F}\quad. \] @@ -366,8 +350,8 @@ \subsubsection{Exercise \label{par:Exercise-additional-6}\ref{par:Exercise-addit C & \bbnum 0 & \text{id} \end{array}\,=\text{split}^{A,B+C}\quad. \] -Show that all polynomial functors $F^{\bullet}$ belong to this typeclass. -Show that exponential functors such as $F^{A}\triangleq Z\rightarrow A$ +Show that all polynomial functors $F$ belong to this typeclass. Show +that exponential functors such as $F^{A}\triangleq Z\rightarrow A$ do not belong to this typeclass. \subsubsection{Exercise \label{par:Exercise-additional-6-1}\ref{par:Exercise-additional-6-1}} @@ -386,25 +370,65 @@ \subsubsection{Exercise \label{par:Exercise-additional-7}\ref{par:Exercise-addit Show that $F$ is a semimonad but not a full monad. Hint: use the flipped Kleisli technique. +\subsubsection{Exercise \label{par:Exercise-additional-7-1}\ref{par:Exercise-additional-7-1}\protect\footnote{See \texttt{\protect\href{https://cstheory.stackexchange.com/questions/54227}{https://cstheory.stackexchange.com/questions/54227}}}} + +A monad $M$\textsf{'}s \lstinline!flatMap! method obeys the naturality law: +\[ +\text{flm}_{M}(g\bef h^{\uparrow M})=\text{flm}_{M}(g)\bef h^{\uparrow M}\quad. +\] +This law holds with an arbitrary function $h:A\rightarrow B$. Show +that one cannot replace $h^{\uparrow M}$ in this law by an arbitrary +function $k:M^{A}\rightarrow M^{B}$. Namely, for some monads $M$ +and some functions $g$, $k$ (of appropriate types) we will have +$\text{flm}_{M}(g\bef k)\neq\text{flm}_{M}(g)\bef k$. + +\subsubsection{Exercise \label{par:Exercise-additional-18-1}\ref{par:Exercise-additional-18-1}} + +Find fully parametric implementations of the type signatures: + +\begin{lstlisting} +def a[A, B, C]: ((C => B) => A) => B => A +def b[A, B]: (((A => B) => B) => B) => A => B +def c[A, B]: ((((A => B) => A) => A) => B) => B +def d[A, B, C]: (((A => B) => C) => A => B) => (B => C) => A => B +def e[A, B, C]: ((B => C) => A => B) => ((A => B) => C) => A => B +\end{lstlisting} + + +\subsubsection{Exercise \label{par:Exercise-additional-18}\ref{par:Exercise-additional-18}} + +Show that the following type signatures have \emph{no} fully parametric +implementations: + +\begin{lstlisting} +def f[A]: Option[A] => A +def g[A, B]: (A => B) => A +def h[A, B]: (A => B) => B => A +def k[A, B, C]: (A => B) => (B => C) => C => A +def l[A, B]: ((((A => B) => B) => A) => B) => B +\end{lstlisting} + +Hint: set some type parameters to the void type (\lstinline!Nothing!). + \subsubsection{Exercise \label{par:Exercise-additional-8}\ref{par:Exercise-additional-8}} Given two fixed types $P$, $Q$ that are not known to be equivalent, consider the contrafunctors $F^{A}\triangleq\left(\left(A\rightarrow P\right)\rightarrow P\right)\rightarrow Q$ and $G^{A}\triangleq A\rightarrow Q$. Show that there exist natural -transformations $F^{\bullet}\leadsto G^{\bullet}$ and $G^{\bullet}\leadsto F^{\bullet}$. -Show that these transformations are \emph{not} isomorphisms. +transformations $F\leadsto G$ and $G\leadsto F$. Show that these +transformations are \emph{not} isomorphisms. \subsubsection{Exercise \label{par:Exercise-additional-9}\ref{par:Exercise-additional-9}} Given two fixed types $P$, $Q$ that are not known to be equivalent, show that the functor $L^{A}\triangleq\left(\left(\left(A\rightarrow P\right)\rightarrow Q\right)\rightarrow Q\right)\rightarrow P$ is a semimonad but not a full monad. (When $P\cong Q$, the functor -$L$ is also not a full monad because $L$ is then equivalent to a -composition of the continuation monad with itself. See Exercise~\ref{subsec:Exercise-monad-composition-mm}.) +$L$ is then equivalent to a composition of the continuation monad +with itself; that is also only a semimonad. See Exercise~\ref{subsec:Exercise-monad-composition-mm}.) \subsubsection{Exercise \label{par:Exercise-additional-9-1}\ref{par:Exercise-additional-9-1}} -For any fully parametric contrafunctor $F^{A}$ that does not explicitly +For any fully parametric contrafunctor $F$ that does not explicitly use the void type\index{void type} ($\bbnum 0$) in its type expression, show that the type of fully parametric functions $\forall A.\,F^{A}\rightarrow A$ is void. Show that the condition of not using the void type is necessary, @@ -415,12 +439,13 @@ \subsubsection{Exercise \label{par:Exercise-additional-9-1}\ref{par:Exercise-add \] The type equivalence $\forall A.\,F^{A}\rightarrow A\cong\bbnum 0$ means that we cannot extract values of type $A$ from a value of type -$F^{A}$. This agrees with the intuition that a contrafunctor type -($F^{A}$) \textsf{``}does not contain\textsf{''} any values of type $A$. +$F^{A}$. This agrees with the intuition that value of type $F^{A}$ +\textsf{``}do not store\textsf{''} any values of type $A$. \subsubsection{Exercise \label{par:Exercise-additional-11}\ref{par:Exercise-additional-11}} -If $M$ is any monad then $M^{A+M^{A}}$ is also a lawful monad. +If $M$ is any monad then $L^{A}\triangleq M^{A+M^{A}}$ is also a +lawful monad. \subsubsection{Exercise \label{par:Exercise-additional-10}\ref{par:Exercise-additional-10}} @@ -434,8 +459,8 @@ \subsubsection{Exercise \label{par:Exercise-additional-12}\ref{par:Exercise-addi \subsubsection{Exercise \label{par:Exercise-additional-13}\ref{par:Exercise-additional-13}} -If $M^{\bullet}$ is a commutative monad and $W$ is a commutative -monoid then the monoid $M^{W}$ is commutative. +If $M$ is a commutative monad and $W$ is a commutative monoid then +the monoid $M^{W}$ is commutative. \subsubsection{Exercise \label{par:Exercise-additional-14}\ref{par:Exercise-additional-14}} @@ -469,18 +494,16 @@ \subsubsection{Exercise \label{par:Exercise-additional-14}\ref{par:Exercise-addi \subsubsection{Exercise \label{par:Exercise-additional-15}\ref{par:Exercise-additional-15}} -Use the Curry-Howard correspondence\index{Curry-Howard correspondence} -and the algorithms LJ or LJT\index{LJT algorithm} to prove that there -exists only one fully parametric function with type signature $\forall A.\,((A\rightarrow A)\rightarrow A)\rightarrow A$, +Simplify the type $\forall A.\,((A\rightarrow A)\rightarrow A)\rightarrow A$, or in Scala: \begin{lstlisting} def f[A]: ((A => A) => A) => A \end{lstlisting} -From that, prove the type equivalence $\forall A.\,((A\rightarrow A)\rightarrow A)\rightarrow A\cong\bbnum 1$. +into a type expression that contains no quantifiers.\footnote{See \texttt{\href{https://cstheory.stackexchange.com/questions/53855/}{https://cstheory.stackexchange.com/questions/53855/}}} \subsubsection{Exercise \label{par:Problem-Peirce-law}\ref{par:Problem-Peirce-law}} -Consider the functor $F^{R}$ defined by: +Consider the functor $F$ defined by: \[ F^{R}\triangleq\forall A.\,((A\rightarrow R)\rightarrow A)\rightarrow A\quad, \] @@ -495,7 +518,7 @@ \subsubsection{Exercise \label{par:Problem-Peirce-law}\ref{par:Problem-Peirce-la \subsubsection{Exercise \label{par:Problem-Peirce-law-1}\ref{par:Problem-Peirce-law-1}} -Consider the profunctor $F^{R,S}$ defined by: +Consider the profunctor $F$ defined by: \[ F^{R,S}\triangleq\forall A.\,((A\rightarrow A)\rightarrow R)\rightarrow S\quad, \] @@ -515,8 +538,7 @@ \subsubsection{Exercise \label{par:Problem-Peirce-law-1}\ref{par:Problem-Peirce- \subsubsection{Exercise \label{par:Problem-Peirce-law-2}\ref{par:Problem-Peirce-law-2}} -Prove the following type equivalences (assuming fixed types $P$, -$Q$, ...): +Prove the following type equivalences (assuming a fixed type $P$): \begin{tabular}{|c|c|} \hline @@ -529,15 +551,13 @@ \subsubsection{Exercise \label{par:Problem-Peirce-law-2}\ref{par:Problem-Peirce- \hline $\forall A.\,(A\rightarrow A)\rightarrow A+P$ & $P$\tabularnewline \hline -$\forall A.\,(A\rightarrow A)\rightarrow A\rightarrow A$ & $\text{List}^{\bbnum 1}$\tabularnewline -\hline -$\forall A.\,((A\rightarrow A)\rightarrow A)\rightarrow P$ & $P$?\tabularnewline +$\forall A.\,((A\rightarrow A)\rightarrow A)\rightarrow P$ & $P$\tabularnewline \hline \end{tabular} \subsubsection{Exercise \label{par:Exercise-additional-16-2}\ref{par:Exercise-additional-16-2}} -Consider the type constructor $F^{R,S}$ defined by: +Consider the type constructor $F$ defined by: \[ F^{R,S}\triangleq\forall A.\,((R\rightarrow A)\rightarrow S)\rightarrow A\quad, \] @@ -559,7 +579,7 @@ \subsubsection{Exercise \label{par:Exercise-additional-16-1}\ref{par:Exercise-ad for discussion about monads having multiple transformers.} \textbf{(a)} Show that codensity monad on $G$ ($\text{Cod}^{G,\bullet}$) -is equivalent to $\text{List}^{\bullet}$ via monad morphisms. +is equivalent to \lstinline!List! via monad morphisms. \textbf{(b)} Show that the corresponding monad transformer: \[ @@ -570,7 +590,7 @@ \subsubsection{Exercise \label{par:Exercise-additional-16-1}\ref{par:Exercise-ad \textbf{(c)} Show that the type constructor $U$ defined by: \[ -U^{M^{\bullet},A}\triangleq\forall R.\,(A\rightarrow G^{M^{R}})\rightarrow G^{M^{R}}=\forall R.\,(A\rightarrow M^{R}\rightarrow M^{R})\rightarrow M^{R}\rightarrow M^{R} +U^{M,A}\triangleq\forall R.\,(A\rightarrow G^{M^{R}})\rightarrow G^{M^{R}}=\forall R.\,(A\rightarrow M^{R}\rightarrow M^{R})\rightarrow M^{R}\rightarrow M^{R} \] is also a lawful monad transformer (with the foreign monad $M$) for the \lstinline!List! monad. Show that the transformer $U$ (known @@ -580,10 +600,10 @@ \subsubsection{Exercise \label{par:Exercise-additional-16-1}\ref{par:Exercise-ad \textbf{(d)} Generalize \textbf{(c)} using an arbitrary (covariant) functor $F$ and two fixed types $P$, $Q$: \[ -V^{F^{\bullet},P,Q,M^{\bullet},A}\triangleq\forall R.\,(A\rightarrow F^{M^{R}}\rightarrow P\times M^{R}+Q)\rightarrow F^{M^{R}}\rightarrow P\times M^{R}+Q\quad. +V^{F,P,Q,M,A}\triangleq\forall R.\,(A\rightarrow F^{M^{R}}\rightarrow P\times M^{R}+Q)\rightarrow F^{M^{R}}\rightarrow P\times M^{R}+Q\quad. \] -Show that there exists a monad morphism $M^{A}\rightarrow V^{F^{\bullet},P,Q,M^{\bullet},A}$, -and that the converse function of type $V^{F^{\bullet},P,Q,M^{\bullet},A}\rightarrow M^{A}$ +Show that there exists a monad morphism $M^{A}\rightarrow V^{F,P,Q,M,A}$, +and that the converse function of type $V^{F,P,Q,M,A}\rightarrow M^{A}$ exists when $Q=\bbnum 0$ (but is \emph{not} a monad morphism). \textbf{(e)} Show that the Church-encoded free monoid on $A$ (see @@ -593,7 +613,7 @@ \subsubsection{Exercise \label{par:Exercise-additional-16-1}\ref{par:Exercise-ad \] can be modified to the type constructor denoted by \lstinline!FMT!: \[ -\text{FMT}^{M^{\bullet},A}\triangleq\forall X^{:\text{Monoid}}.\,(A\rightarrow M^{X})\rightarrow M^{X}\quad, +\text{FMT}^{M,A}\triangleq\forall X^{:\text{Monoid}}.\,(A\rightarrow M^{X})\rightarrow M^{X}\quad, \] which is a lawful monad transformer (with the foreign monad $M$) for the \lstinline!List! monad. Show that this transformer is not @@ -650,7 +670,7 @@ \subsubsection{Problem \label{subsec:Problem-co-pointed-applicative}\ref{subsec: applicative and co-pointed but fails the compatibility law. Does there exist any co-pointed applicative functor that satisfies the law~(\ref{eq:compatibility-law-of-extract-and-zip}) but is \emph{not} of the form $A\times G^{A}$ with some applicative -functor $G^{\bullet}$? +functor $G$? \subsubsection{Problem \label{par:Problem-monads-2}\ref{par:Problem-monads-2}} @@ -676,11 +696,11 @@ \subsubsection{Problem \label{par:Problem-identity-natural-monad-morphism}\ref{p Are there any monadically natural monad morphisms $M\leadsto M$ that are not identity functions? (Equivalently, any non-identical natural -transformations $\text{Id}^{\bullet}\leadsto\text{Id}^{\bullet}$ -between identity functors in the category of monads?) If it were possible -to prove that any natural monad morphism $M\leadsto M$ equals an -identity function, there would be no need to verify the non-degeneracy -law for monad transformers\textsf{'} base runners (see page~\pageref{par:Open-question-monad-id-trans}). +transformations $\text{Id}\leadsto\text{Id}$ between identity functors +in the category of monads?) If it were possible to prove that any +natural monad morphism $M\leadsto M$ equals an identity function, +there would be no need to verify the non-degeneracy law for monad +transformers\textsf{'} base runners (see page~\pageref{par:Open-question-monad-id-trans}). We look for a monad morphism $\varepsilon^{M,A}:M^{A}\rightarrow M^{A}$ that is defined for all monads $M$ and is monadically natural in @@ -695,7 +715,8 @@ \subsubsection{Problem \label{par:Problem-identity-natural-monad-morphism}\ref{p are arbitrary monads, and $\phi:M\leadsto N$ is an arbitrary monad morphism. We need to prove that any such $\varepsilon$ must be an identity function, $\varepsilon=\text{id}^{:M^{A}\rightarrow M^{A}}$, -or to find an example of such $\varepsilon$ not equal to identity.\footnote{See discussion here: \texttt{\href{https://stackoverflow.com/questions/61444425/}{https://stackoverflow.com/questions/61444425/}}} +or to find an example of such $\varepsilon$ not equal to identity.\footnote{See discussion here: \texttt{\href{https://stackoverflow.com/questions/61444425/}{https://stackoverflow.com/questions/61444425/}} +for a solution.} \subsubsection{Problem \label{par:Problem-monads-3}\ref{par:Problem-monads-3}} @@ -863,7 +884,7 @@ \subsubsection{Problem \label{par:Problem-monads-5-2-1}\ref{par:Problem-monads-5 is equal to $t\triangleright...$, and it seems impossible to convert one into another, given that $h$ and $t$ are arbitrary values. -Note that +Note that: \[ \text{pu}_{M}(r^{:R})\oplus_{M}q^{:M^{R}}=r\triangleright\gunderline{\text{pu}_{M}\triangleright\text{flm}_{M}}(u\rightarrow q\triangleright(v\rightarrow u\oplus_{R}v)^{\uparrow M})=q\triangleright(v\rightarrow r\oplus_{R}v)^{\uparrow M}\quad. \] @@ -897,8 +918,8 @@ \subsubsection{Problem \label{subsec:Problem-monatron-lift-reset-and-shift}\ref{ \subsubsection{Problem \label{subsec:Problem-unique-functor-liftings}\ref{subsec:Problem-unique-functor-liftings}} -For any fully parametric type constructor $P^{A}$ covariant in $A$, -the lifting of a function $f^{:A\rightarrow B}$ to $P$ is performed +For any fully parametric and covariant type constructor $P$, the +lifting of a function $f^{:A\rightarrow B}$ to $P$ is performed via the \lstinline!fmap! method of $P$, so that \lstinline!fmap(f)! is a function of type $P^{A}\rightarrow P^{B}$ denoted by $f^{\uparrow F}$ in this book. The standard code of \lstinline!fmap! is defined by diff --git a/sofp-src/tex/sofp-transformers.tex b/sofp-src/tex/sofp-transformers.tex index 2be999a28..f3dc4f95d 100644 --- a/sofp-src/tex/sofp-transformers.tex +++ b/sofp-src/tex/sofp-transformers.tex @@ -2727,7 +2727,7 @@ \subsection{Summary of the laws of monad transformers\label{subsec:Laws-of-monad In total, we found 18 laws for monad transformers. Are all these laws necessary? -The main use of the laws is to verify correctness of the code. The +The main use of the laws is to assure correctness of the code. The next section shows some examples of incorrect implementations of monad transformers and indicates the laws that are violated. @@ -2808,28 +2808,26 @@ \subsection{Examples of failure to define a general monad transformer\label{subs \paragraph{Functor co-product} -The co-product $L^{\bullet}+M^{\bullet}$ is in general not a monad -when $L$ and $M$ are arbitrary monads. A counterexample is found -by using two \lstinline!Reader! monads, $L^{A}\triangleq P\rightarrow A$ -and $M^{A}\triangleq Q\rightarrow A$: the co-product $\left(P\rightarrow A\right)+\left(Q\rightarrow A\right)$ +The co-product $L+M$ is in general not a monad when $L$ and $M$ +are arbitrary monads. A counterexample is found by using two \lstinline!Reader! +monads, $L^{A}\triangleq P\rightarrow A$ and $M^{A}\triangleq Q\rightarrow A$: +the co-product $\left(P\rightarrow A\right)+\left(Q\rightarrow A\right)$ is not a monad (Exercise~\ref{subsec:Exercise-1-monads-6}). But -even when $L^{\bullet}+M^{\bullet}$ is a monad, the identity law -is violated: with $M=\text{Id}$, the monad $L+\text{Id}$ is not -equivalent to $L$. +even when $L+M$ is a monad, the identity law is violated: with $M=\text{Id}$, +the monad $L+\text{Id}$ is not equivalent to $L$. \paragraph{Using the free monad} -The functor composition $L^{M^{\bullet}}$ and the co-product $L^{\bullet}+M^{\bullet}$ -may not always be monads, but they are always functors. We can make -monads out of those functors via the free monad construction. We obtain -$\text{Free}^{L^{M^{\bullet}}}$, the free monad on $L^{M^{\bullet}}$, -and $\text{Free}^{L^{\bullet}+M^{\bullet}}$, the free monad on $L^{\bullet}+M^{\bullet}$. -Many laws of the monad transformers are satisfied by these constructions. -However, the identity laws fail: +The functor composition $L\circ M$ and the co-product $L+M$ may +not always be monads, but they are always functors. We can make monads +out of those functors via the free monad construction. We obtain $\text{Free}^{L\circ M}$, +the free monad on $L\circ M$, and $\text{Free}^{L+M}$, the free +monad on the functor co-product $L+M$. Many laws of the monad transformers +are satisfied by these constructions. However, the identity laws fail: \[ -\text{Free}^{L^{\text{Id}^{\bullet}}}\cong\text{Free}^{L^{\bullet}}\not\cong L\quad,\quad\quad\text{Free}^{L^{\bullet}+\text{Id}^{\bullet}}\not\cong L\quad. +\text{Free}^{L\circ\text{Id}}\cong\text{Free}^{L}\not\cong L\quad,\quad\quad\text{Free}^{L+\text{Id}}\not\cong L\quad. \] -The lifting laws are also violated because $\text{flift}:M^{A}\rightarrow\text{Free}^{L^{\bullet}+M^{\bullet},A}$ +The lifting laws are also violated because $\text{flift}:M^{A}\rightarrow\text{Free}^{L+M,A}$ is not a monad morphism (it maps $\text{pu}_{M}$ into a non-pure value of the free monad). Nevertheless, these constructions are not useless. Once we run the free monad into a concrete (non-free) monad, @@ -2940,12 +2938,12 @@ \subsection{Examples of failure to define a general monad transformer\label{subs value ($1+\bbnum 0^{:Z\rightarrow B}$), which loses information and violates the identity law of monad morphisms. -\textbf{(2)} The composition of $\text{Cod}^{L,\bullet}$ and $M^{\bullet}$ +\textbf{(2)} The composition of $\text{Cod}^{L,\bullet}$ and $M$ yields the following type constructor: \[ F^{A}\triangleq\forall B.\,(M^{A}\rightarrow L^{B})\rightarrow L^{B}\quad. \] -However, $F^{A}$ fails to be a monad for all $L$ and $M$. A counterexample +However, $F$ fails to be a monad for all $L$ and $M$. A counterexample is $L^{A}\triangleq S\rightarrow A$ and $M^{A}\triangleq R\rightarrow A$, for which the Yoneda identity gives: \begin{align*} @@ -4825,7 +4823,7 @@ \subsubsection{Statement \label{subsec:Statement-composition-rigid-monads}\ref{s a lawful monad transformer. Since the transformers for $R_{1}$ and $R_{2}$ are of the composed-outside kind, $T_{R_{1}}^{M}=R_{1}\circ M$ and $T_{R_{2}}^{M}=R_{2}\circ M$, the stack of transformers is expressed -as +as: \[ T_{R_{1}}^{T_{R_{2}}^{M}}=R_{1}\circ T_{R_{2}}^{M}=R_{1}\circ(R_{2}\circ M)=R_{1}\circ R_{2}\circ M\quad. \] diff --git a/sofp-src/tex/sofp-traversable.tex b/sofp-src/tex/sofp-traversable.tex index 60107ac8e..990beafbb 100644 --- a/sofp-src/tex/sofp-traversable.tex +++ b/sofp-src/tex/sofp-traversable.tex @@ -13,9 +13,11 @@ \section{Motivation} chapters examined systematically the properties of functions used in that style (\lstinline!map!, \lstinline!filter!, \lstinline!flatMap!, and \lstinline!zip!) and generalized them to many different type -constructors. This chapter adopts the same approach to study and generalize -the \lstinline!reduce! method. In this way, we will conclude the -study of the \lstinline!map!/\lstinline!reduce! programming style. +constructors. This chapter adopts the same approach to examine and +generalize the \lstinline!reduce! method, which will lead to the +concept of \textsf{``}traversable functors\textsf{''}. In this way, we will complete +a theoretical study of the \lstinline!map!/\lstinline!reduce! programming +style. \subsection{From \texttt{reduce} and \texttt{foldLeft} to \texttt{foldMap} and \texttt{traverse}\label{subsec:From-reduce-and-foldleft-to-foldmap}} @@ -1347,13 +1349,17 @@ \subsection{Recursion schemes. I. Folding operations\label{subsec:Recursion-sche and \lstinline!fold(f)(x)! is guaranteed to terminate for any terminating function $f:S^{A,Z}\rightarrow Z$. -Instead of working with the general function $\text{fold}_{S}$ and -defining all recursive types via \lstinline!Fix!, it is more convenient -to implement and use specialized versions of $\text{fold}_{S}$ for -already defined recursive types. The general implementation of $\text{fold}_{S}$ -in Eq.~(\ref{eq:fold-via-recursion-scheme-1}) can be translated -mechanically (e.g., using macros or code generators) into code specialized -for a given data type and recursion scheme. +The method $\text{fold}_{S}$ works in the same way for all recursion +schemes $S$. This makes $\text{fold}_{S}$ powerful but hard to use +because we need to work with data structures defined via the \lstinline!Fix! +type constructor. Instead of working with the general function $\text{fold}_{S}$ +and defining all recursive types via \lstinline!Fix!, it is more +convenient to implement and use a specialized code of $\text{fold}_{S}$ +adapted to the data structure at hand. The general implementation +of $\text{fold}_{S}$ in Eq.~(\ref{eq:fold-via-recursion-scheme-1}) +can be translated mechanically (in principle, even automatically using +macros or code generators) into code specialized for a given data +type and recursion scheme. For instance, while the type \lstinline!TreeN[A]! is equivalent to \lstinline!Fix[S3[A, *]]! shown above, it is easier to work with @@ -1376,10 +1382,10 @@ \subsection{Recursion schemes. I. Folding operations\label{subsec:Recursion-sche def printLaTeX[A](tree: TreeN[A]): String = "\\Tree" + foldTreeN[A, String](toLaTeX3)(tree) \end{lstlisting} -Another simple example of an aggregation operation that cannot be -expressed as a traversal is the task of determining the maximum branching -number of a given rose tree. The function \lstinline!foldTreeN! now -allows us to implement that: +An example of an aggregation operation that cannot be expressed as +a traversal is the task of determining the maximum branching number +of a given rose tree. The function \lstinline!foldTreeN! now allows +us to implement that: \begin{lstlisting} def maxBranching[A]: TreeN[A] => Int = foldTreeN[A, Int] { case Left(_) => 0 @@ -1391,8 +1397,109 @@ \subsection{Recursion schemes. I. Folding operations\label{subsec:Recursion-sche res2: Int = 2 \end{lstlisting} +Another example of an operation not expressible via \lstinline!traverse! +is \lstinline!zipWithDepth!, which we implemented in Section~\ref{subsec:Tasks-not-implementable-via-traverse} +through custom code. We will now implement \lstinline!zipWithDepth! +via $\text{fold}_{S}$ specialized to the binary tree recursion scheme +$S^{A,R}\triangleq A+R\times R$ that defines the tree type constructor +\lstinline!T2!. The code for the specialized version of $\text{fold}_{S}$ +is: +\begin{lstlisting} +type S[A, R] = Either[A, (R, R)] // Recursion scheme for T2[A]. +def foldT2[A, Z](f: S[A, Z] => Z): T2[A] => Z = { + case Leaf(a) => f(Left(a)) + case Branch(l, r) => f(Right((foldT2(f)(l), foldT2(f)(r)))) +} +\end{lstlisting} -\subsection{Recursion schemes. II. Unfolding operations} +The function call \lstinline!foldT2(f)! will be able to convert any +tree into a value of type \lstinline!Z! if we supply a function \lstinline!f! +of type $A+Z\times Z\rightarrow Z$. It appears that we need the type +\lstinline!Z! to contain a new depth-indexed tree. The function \lstinline!f! +will be applied recursively to leaves and branches of a tree. The +depth-indexed index can be computed correctly only if the type \lstinline!Z! +somehow keeps track of the current depth in the tree. So, we cannot +just set \lstinline!Z = T2[(A, Int)]!. We need to use a \lstinline!State! +monad whose internal state (of type \lstinline!Int!) represents the +current depth and is updated automatically. Let us define a suitable +\lstinline!State! monad that we will denote by \lstinline!St! for +brevity: +\begin{lstlisting} +final case class St[A](run: Int => (A, Int)) { // A State monad with internal state of type Int. + import io.chymyst.ch.implement // Derive these methods automatically from types. + def flatMap[B](f: A => St[B]): St[B] = implement + def map[B](f: A => B): St[B] = implement +} +def incrementAndGet: St[Int] = St(s => (s + 1, s + 1)) // Increment the current state value. +def get: St[Int] = St(s => (s, s)) // Fetch the current state value. +def set(s: Int): St[Unit] = St(_ => ((), s)) // Set the state, ignore previous state value. +\end{lstlisting} +The folding result type \lstinline!Z! will be defined as \lstinline!Z = St[T2[(A, Int)]]!. +The wrapped value (of type \lstinline!T2[(A, Int)]!) inside the \lstinline!State! +monad will be the final decorated tree. To extract that tree, we will +need to build a \lstinline!State! monad program and run it with the +initial depth value \lstinline!0!. So, we expect the code of \lstinline!zipWithDepth! +to look like this: +\begin{lstlisting} +def zipWithDepth[A](tree: T2[A]): T2[(A, Int)] = + foldT2[A, St[T2[(A, Int)]]](f).run(0)._1 +\end{lstlisting} + +It remains to implement a suitable function \lstinline!f! to which +we will apply \lstinline!foldT2!. The value of the internal state +represents the current depth of the tree element. We need to increment +that depth whenever we find a branch and then traverse the two subtrees +starting from the same depth value. The final code is: +\begin{lstlisting} +def zipWithDepth[A](tree: T2[A]): T2[(A, Int)] = foldT2[A, St[T2[(A, Int)]]] { + case Left(a) => for { s <- get } yield Leaf((a, s)) // Put the current depth into the Leaf value. + case Right((l, r)) => for { + s <- incrementAndGet // Read the current depth after incrementing it. + x <- l // Traverse the left branch starting from depth `s`. + _ <- set(s) // Set the same initial depth `s` for traversing the right branch. + y <- r // Traverse the right branch. + } yield Branch(x, y) +}(tree).run(0)._1 +\end{lstlisting} +Here we need to use the \lstinline!State! monad\textsf{'}s method \lstinline!set(s)! +with a value \lstinline!s! obtained from a previous monadic computation. + +To test the code, apply \lstinline!zipWithDepth! to a sample tree +\lstinline!t2! used earlier in Sections~\ref{subsec:Decorating-a-tree-breadth-first-traversal} +and~\ref{subsec:Tasks-not-implementable-via-traverse}: + +\begin{wrapfigure}{l}{0.78\columnwidth}% +\vspace{-0.75\baselineskip} +\begin{lstlisting} +scala> zipWithDepth(t2) +res0: T2[(Int, Int)] = Branch(Leaf((8, 1)), Branch(Branch(Leaf((3, 3)), Leaf((5, 3))), Leaf((4, 2)))) +\end{lstlisting} + +\vspace{-0.5\baselineskip} +\end{wrapfigure}% + +\noindent ~{\tiny{}\Tree[ (8,1) [ [ (3,3) (5,3) ] (4,2) ] ]} + +\noindent ~ + +Note that the the actually used type of \lstinline!foldT2! is somewhat +similar to the type of \lstinline!traverse!: +\begin{align*} + & \text{foldT2}:\big(S^{A,F^{L^{B}}}\rightarrow F^{L^{B}}\big)\rightarrow L^{A}\rightarrow F^{L^{B}}\quad,\\ + & \text{trav}_{L}:(A\rightarrow F^{B})\rightarrow L^{A}\rightarrow F^{L^{B}}\quad. +\end{align*} +(Here $L=\text{T2}$ and $S$ is its recursion scheme.) We apply \lstinline!foldT2! +to a function $f:S^{A,F^{L^{B}}}\rightarrow F^{L^{B}}$ whose $F$-effect +is \emph{not} equivalent to an applicative functor\textsf{'}s effect: The function +$f$ assigns the \lstinline!State! monad\textsf{'}s internal state to a previously +computed value, which applicative effects cannot depend on a previous +value. A recursion scheme-based folding ($\text{fold}_{S}$) can use +arbitrary functors $F$, including monads. This is more powerful than +the plain traversal, $\text{trav}_{L}(f^{:A\rightarrow F^{B}})$, +that may only use applicative functors $F$. Because of that extra +power, we were able to implement \lstinline!zipWithDepth! via $\text{fold}_{S}$. + +\subsection{Recursion schemes. II. Unfolding operations\label{subsec:Recursion-schemes.-II.unfolding}} A folding operation converts a collection to a single value. The opposite operation is \textsf{``}unfolding\textsf{''}: converting a single value into a collection. @@ -1711,118 +1818,6 @@ \subsubsection{Example \label{subsec:Example-unfold-tree-evenodd}\ref{subsec:Exa \noindent ~{\tiny{}\Tree[ 1 [ 2 [3 [ 4 -1 ] ] ] ]} -\subsection{Recursion schemes. III. Traversing operations} - -Folding with a recursion scheme ($\text{fold}_{S}$) allows us to -implement operations such as \lstinline!printLaTeXSubtree! (Section~\ref{subsec:Recursion-schemes.-folding}) -that cannot be expressed via ordinary \lstinline!fold! or \lstinline!traverse! -functions. Another operation not expressible via \lstinline!traverse! -is \lstinline!zipWithDepth!, which we implemented in Section~\ref{subsec:Tasks-not-implementable-via-traverse} -through custom code. We will now implement \lstinline!zipWithDepth! -via a more general traversal operation ($\text{trav}_{S}$) parameterized -by an arbitrary recursion scheme $S$ and an arbitrary functor $F$ -(not necessarily applicative!). - -To figure out the type signature of $\text{trav}_{S}$, consider the -relationship between \lstinline!foldMap!, $\text{fold}_{S}$, and -the ordinary \lstinline!traverse! (denoted $\text{trav}_{L}$): -\begin{align*} - & \text{foldMap}_{L}(f^{:A\rightarrow Z}):L^{A}\rightarrow Z\quad,\quad\quad\text{trav}_{L}(f^{:A\rightarrow F^{B}}):L^{A}\rightarrow F^{L^{B}}\quad,\\ - & \text{fold}_{S}(f^{:S^{A,Z}\rightarrow Z}):L^{A}\rightarrow Z\quad,\quad\quad\text{trav}_{S}(f^{:???}):L^{A}\rightarrow F^{L^{B}}\quad. -\end{align*} -We recover \lstinline!foldMap! from \lstinline!traverse! by setting -the applicative functor $F$ as $F^{B}\triangleq Z$. So, we expect -to obtain the type signature of $\text{trav}_{S}$ from the type signature -of $\text{fold}_{S}$ if we replace $Z$ by $F^{L^{B}}$. The first -argument $f$ of $\text{trav}_{S}$ will then have the type $S^{A,F^{L^{B}}}\rightarrow F^{L^{B}}$: -\[ -\text{trav}_{S}:(S^{A,F^{L^{B}}}\rightarrow F^{L^{B}})\rightarrow L^{A}\rightarrow F^{L^{B}}\quad. -\] -To implement this method, begin with the type equivalence $L^{A}\cong S^{A,L^{A}}$. -We can apply $\text{trav}_{S}(f)$ recursively to the values of type -$L^{A}$ stored inside $S^{A,L^{A}}$ and obtain a value of type $S^{A,F^{L^{B}}}$: -\[ -\big(s^{:S^{A,L^{A}}}\triangleright\,\overline{\text{trav}_{S}(f)}^{\uparrow S^{A,\bullet}}\big):S^{A,F^{L^{B}}}\quad. -\] -It remains to apply $f$ to the last obtained value. This completes -the code of $\text{trav}_{S}$: -\[ -\text{trav}_{S}\big(f^{:S^{A,F^{L^{B}}}\rightarrow F^{L^{B}}}\big)\triangleq\overline{\text{trav}_{S}(f)}^{\uparrow S^{A,\bullet}}\bef f\quad. -\] - -The method $\text{trav}_{S}$ works in the same way for all recursion -schemes $S$ and for all type constructors $F$. This makes $\text{trav}_{S}$ -powerful but hard to use because we need to work with data structures -defined via the \lstinline!Fix! type constructor. It is easier to -use a specialized version of $\text{trav}_{S}$ for the data structure -at hand, similarly to what we did in the previous sections for folding -and unfolding. - -To illustrate this, let us implement \lstinline!zipWithDepth! via -$\text{trav}_{S}$ with a binary tree recursion scheme. We will use -the \lstinline!State! monad as the functor $F$: -\begin{lstlisting} -final case class St[A](run: Int => (A, Int)) { // A State monad with internal state of type Int. - import io.chymyst.ch.implement // Derive these methods automatically from types. - def flatMap[B](f: A => St[B]): St[B] = implement - def map[B](f: A => B): St[B] = implement -} -def incrementAndGet: St[Int] = St(s => (s + 1, s + 1)) // Increment the current state value. -def get: St[Int] = St(s => (s, s)) // Fetch the current state value. -def set(s: Int): St[Unit] = St(_ => ((), s)) // Set the state, ignore previous state value. -\end{lstlisting} -Next, define the recursion scheme $S^{A,R}$ and a specialized version -of $\text{trav}_{S}$ for trees of type \lstinline!T2[A]!: -\begin{lstlisting} -type S[A, R] = Either[A, (R, R)] // Recursion scheme for T2[A]. -def travT2[A, B, F[_]](f: S[A, F[T2[B]]] => F[T2[B]]): T2[A] => F[T2[B]] = { - case Leaf(a) => f(Left(a)) - case Branch(l, r) => f(Right((travT2(f)(l), travT2(f)(r)))) -} -\end{lstlisting} -It remains to apply \lstinline!travT2! to a suitable function \lstinline!f!. -The value of the internal state will represent the current depth of -the tree element. We need to increment the depth whenever we find -a branch and then traverse the two subtrees starting from the same -depth value. The code is: -\begin{lstlisting} -def zipWithDepth[A](tree: T2[A]): T2[(A, Int)] = travS[A, (A, Int), St] { - case Left(a) => for { s <- get } yield Leaf((a, s)) // Put the current depth into the Leaf value. - case Right((l, r)) => for { - s <- incrementAndGet // Read the current depth after incrementing it. - x <- l // Traverse the left branch starting from depth `s`. - _ <- set(s) // Set the same initial depth `s` for traversing the right branch. - y <- r // Traverse the right branch. - } yield Branch(x, y) -}(tree).run(0)._1 -\end{lstlisting} -To test the code, apply \lstinline!zipWithDepth! to a sample tree -\lstinline!t2! used earlier in Sections~\ref{subsec:Decorating-a-tree-breadth-first-traversal} -and~\ref{subsec:Tasks-not-implementable-via-traverse}: - -\begin{wrapfigure}{l}{0.78\columnwidth}% -\vspace{-0.75\baselineskip} -\begin{lstlisting} -scala> zipWithDepth(t2) -res0: T2[(Int, Int)] = Branch(Leaf((8, 1)), Branch(Branch(Leaf((3, 3)), Leaf((5, 3))), Leaf((4, 2)))) -\end{lstlisting} - -\vspace{-0.5\baselineskip} -\end{wrapfigure}% - -\noindent ~{\tiny{}\Tree[ (8,1) [ [ (3,3) (5,3) ] (4,2) ] ]} - -\noindent ~ - -Here we need to use the \lstinline!State! monad\textsf{'}s method \lstinline!set(s)! -with a value \lstinline!s! obtained from a previous monadic computation. -So, the code involves $\text{trav}_{S}(f)$ with a function $f:S^{A,F^{L^{B}}}\rightarrow F^{L^{B}}$ -whose $F$-effect is \emph{not} equivalent to an applicative functor\textsf{'}s -effect (which cannot depend on a previously computed value). We can -see that the recursion scheme-based traversal ($\text{trav}_{S}$) -is more powerful than the plain traversal, $\text{trav}_{L}(f^{:A\rightarrow F^{B}})$, -that may only use $F$\textsf{'}s applicative functor methods. - \subsection{Exercises\index{exercises}} \subsubsection{Exercise \label{subsec:Exercise-traversables-7-1}\ref{subsec:Exercise-traversables-7-1}} @@ -4156,6 +4151,164 @@ \subsubsection{Exercise \label{subsec:Exercise-traversables-10-1-1-2}\ref{subsec \section{Discussion and further developments} +\subsection{Recursion schemes and structure-preserving maps} + +Sections~\ref{subsec:Recursion-schemes.-folding} and~\ref{subsec:Recursion-schemes.-II.unfolding} +show the general \textsf{``}folding\textsf{''} and \textsf{``}unfolding\textsf{''} operations that +work in the same way for all recursion schemes. It turns out that +those operations are mathematically unique. We will now prove that +there is only one \textsf{``}folding\textsf{''} function and only one \textsf{``}unfolding\textsf{''} +function obeying appropriate laws. + +What are those laws? The \textsf{``}folding\textsf{''} function had the type signature: +\[ +\text{fold}_{S}:(S^{A,Z}\rightarrow Z)\rightarrow L^{A}\rightarrow Z\quad. +\] +Given $f:S^{A,Z}\rightarrow Z$, the function $\text{fold}_{S}(f)$ +transforms a value of type $L^{A}$ into a value of type $Z$. Here, +the type $L^{A}$ is defined recursively via the equation $L^{A}\triangleq S^{A,L^{A}}$ +while the type $Z$ is arbitrary. + +It is actually not so important that $L^{A}$ is a data structure +with a type parameter, because the code of $\text{fold}_{S}$ always +keeps $A$ fixed. So, we may simplify our considerations by eliminating +the type parameter $A$. We replace the recursion scheme $S^{A,R}$ +by just $S^{R}$ (where $S$ is a functor) and the type $L^{A}$ by +a type $T$ defined by the recursive equation $T\triangleq S^{T}$. +Then the type of $\text{fold}_{S}$ is simplified to: +\[ +\text{fold}_{S}:(S^{Z}\rightarrow Z)\rightarrow T\rightarrow Z\quad. +\] + +At first sight, the types $T$ and $Z$ have nothing in common. However, +we need to keep in mind that $T$ is equivalent to $S^{T}$ by definition +of the recursive type $T$. The equivalent types $T$ and $S^{T}$ +are related by a pair of isomorphisms that we may call \lstinline!fix! +and \lstinline!unfix!: +\[ +\text{fix}:S^{T}\rightarrow T\quad,\quad\text{unfix}:T\rightarrow S^{T}\quad,\quad\text{fix}\bef\text{unfix}=\text{id}\quad,\quad\text{unfix}\bef\text{fix}=\text{id}\quad. +\] +We consider the functions \lstinline!fix! and \lstinline!unfix! +as given. The type signature of $\text{fold}_{S}$ requires a function +of type $S^{Z}\rightarrow Z$, which is similar to the type of \lstinline!fix!. +So, inside the function body of $\text{fold}_{S}(f)$, we have types +$T$ and $Z$ for which functions of types $S^{T}\rightarrow T$ and +$S^{Z}\rightarrow Z$ are available. + +It is useful at this point to borrow some terminology from category +theory where an object $Z$ with a morphism of type $S^{Z}\rightarrow Z$ +has a special name. The object $Z$ is called a \textbf{functor algebra} +on the functor $S$, and\index{functor algebra} the morphism of type +$S^{Z}\rightarrow Z$ is called the algebra\textsf{'}s \textbf{structure map}.\index{functor algebra!structure map}\footnote{In the mathematical literature, a functor algebra on a functor $F$ +is commonly referred to as an $F$\textbf{-algebra}\index{$F$-algebra!see functor algebra}. +That terminology is rather inconvenient because the relevant functor +is not always called $F$.} + +From a programmer\textsf{'}s viewpoint, an intuitive picture is that the type +$Z$ must be able somehow to absorb information from the type $S^{Z}$. +For instance, if $S$ is a recursion scheme for a binary tree (such +as, $S^{R}=A+R\times R$ for \lstinline!T2!) then the type $S^{Z}\rightarrow Z$ +is equivalent to the product type $(A\rightarrow Z)\times(Z\times Z\rightarrow Z)$. +So, $Z$ must support an operation that maps a tree leaf (of some +fixed type $A$) into a value of type $Z$, and also an operation +that maps two tree branches (both of type $Z$) into another value +of type $Z$. Those operations are automatically \textsf{``}induced\textsf{''} by +the recursion scheme $S$. The type $T$ must also support the same +operations. + +So, both types $T$ and $Z$ have the property of being functor algebras +on $S$. In other words, both types $T$ and $Z$ have operations +induced by the recursion scheme $S$. It is then natural to expect +that a useful map between values of types $T$ and $Z$ should preserve +the structure of those operations. The mathematical formulation of +that requirement is called the law of \textsf{``}\index{functor algebra!morphism law}functor +algebra morphisms\textsf{''}. + +\subsubsection{Definition \label{subsec:Definition-functor-algebra}\ref{subsec:Definition-functor-algebra}} + +Given a functor $S$ and two $S$-functor algebras $X,$$Y$, with +given structure maps $p_{X}:S^{X}\rightarrow X$ and $p_{Y}:S^{Y}\rightarrow Y$, +a function $f:X\rightarrow Y$ is called a \textbf{functor algebra +morphism}\index{functor algebra morphism} if the following law holds: +\[ +\xymatrix{\xyScaleY{1.4pc}\xyScaleX{3.0pc}S^{X}\ar[r]\sp(0.5){\ p_{X}}\ar[d]\sp(0.45){\,f^{\uparrow S}} & X\ar[d]\sp(0.45){\,f}\\ +S^{Y}\ar[r]\sp(0.5){~p_{Y}} & Y +} +\] +\begin{equation} +p_{X}\bef f=f^{\uparrow S}\bef p_{Y}\quad.\label{eq:p-algebra-morphism-law-1} +\end{equation} +$\square$ + +Suppose we are given a function $p_{Z}:S^{Z}\rightarrow Z$, which +makes a type $Z$ into an $S$-functor algebra. Then it turns out +that $\text{fold}_{S}(p_{Z}):T\rightarrow Z$ is a functor algebra +morphism between $T$ and $Z$. In other words, the function $\text{fold}_{S}(p_{Z})$ +preserves the operations induced by the recursion scheme $S$. + +\subsubsection{Statement \label{subsec:Statement-catamorphism}\ref{subsec:Statement-catamorphism}} + +Consider a functor $S$ and a recursive type $T$ defined by $T\triangleq S^{T}$. +The functions \lstinline!fix! $:S^{T}\rightarrow T$ and \lstinline!unfix! +$:T\rightarrow S^{T}$ are assumed to be given. For any type $Z$ +and any function $g:S^{Z}\rightarrow Z$, define $f:T\rightarrow Z$ +by $f\triangleq\text{fold}_{S}(g)$, where $\text{fold}_{S}$ is defined +in Section~\ref{subsec:Recursion-schemes.-folding}. Then $f$ is +a functor algebra morphism: it obeys Eq.~(\ref{eq:p-algebra-morphism-law-1}) +with $X=T$, $p_{X}=\text{fix}$, $Y=Z$, $p_{Y}=g$. + +\subparagraph{Proof} + +The functor algebra morphism law~(\ref{eq:p-algebra-morphism-law-1}) +of $f$ is: +\[ +\text{fix}\bef f\overset{?}{=}f^{\uparrow S}\bef g\quad. +\] +Write the code of $f\triangleq\text{fold}_{S}(g)$: +\[ +f:T\rightarrow Z\quad,\quad\quad f\triangleq\text{unfix}\bef\overline{f}^{\uparrow S}\bef g\quad. +\] +Pre-compose \lstinline!fix! to both sides of the equation for $f$: +\begin{align*} + & \text{fix}\bef f=\gunderline{\text{fix}\bef\text{unfix}}\bef\overline{f}^{\uparrow S}\bef g\\ +{\color{greenunder}\text{recall that }\text{fix}\bef\text{unfix}=\text{id}:}\quad & =\overline{f}^{\uparrow S}\bef g\quad. +\end{align*} +We have obtained the required law. $\square$ + +A similar property holds for the function \lstinline!unfold!. For +any function $g:Z\rightarrow S^{Z}$ we define $f:Z\rightarrow T$ +by $f\triangleq\text{unfold}_{S}(g)$. Then we will have: +\[ +f\bef\text{unfix}=g\bef f^{\uparrow S}\quad. +\] +This property is called the \textsf{``}$S$-\index{functor coalgebra morphism law}\textbf{functor +coalgebra morphism} law\textsf{''} in category theory. Intuitively, it means +that the function $f$, in a certain precise sense, preserves the +operations required by the recursion scheme $S$. + +The proof is analogous to that of Statement~\ref{subsec:Statement-catamorphism}. +Write the code of $f\triangleq\text{unfold}_{S}(g)$: +\[ +f\triangleq g\bef f^{\uparrow S}\bef\text{fix}\quad. +\] +It remains to compose \lstinline!unfix! with both sides of the equation +above: +\[ +f\bef\text{unfix}=g\bef f^{\uparrow S}\bef\gunderline{\text{fix}\bef\text{unfix}}=g\bef f^{\uparrow S}\quad. +\] +This proves the functor coalgebra morphism law for $f$. + +Note that these properties of $\text{fold}_{S}(g)$ and $\text{unfold}_{S}(g)$ +hold only under the assumption that the resulting functions terminate. +The proofs do not show that those functions must terminate. As we +have seen, functions obtained via \lstinline!fold! and \lstinline!unfold! +will in some cases loop forever. The laws only hold when the functions +do not generate infinite loops. + +In the mathematical literature, the function $\text{fold}_{S}$ is +called a \textbf{catamorphism}\index{catamorphism}, and $\text{unfold}_{S}$ +is called an \index{anamorphism}\textbf{anamorphism}. + \subsection{The missing laws of \texttt{traverse} and \texttt{zipWithIndex}\label{subsec:Laws-of-traverse-and-zipWithIndex}} The two laws of \lstinline!traverse! shown in this chapter do \emph{not} @@ -4661,11 +4814,10 @@ \subsection{Traversals for nested recursive types} In most of this book, recursive type constructors are defined via equations of the form $L^{A}\triangleq S^{A,L^{A}}$, where $S$ is a recursion scheme. We have seen one example of a recursive type constructor -(a perfect-shaped tree, Sections~\ref{subsec:Perfect-shaped-trees} -and~\ref{subsec:Example-traversal-perfect-shaped-tree}) that cannot -be defined in this way. The reason is that the recursive type equation -for a perfect-shaped binary tree\index{perfect-shaped tree} $\text{PT}$ -is: +that cannot be defined in this way: a perfect-shaped tree (see Section~\ref{subsec:Perfect-shaped-trees} +and Example~\ref{subsec:Example-traversal-perfect-shaped-tree}). +The recursive type equation for a perfect-shaped binary tree\index{perfect-shaped tree} +($\text{PT}$) is: \begin{equation} \text{PT}^{A}\triangleq A+\text{PT}^{A\times A}\quad.\label{eq:perfect-shaped-binary-tree-type-equation} \end{equation} @@ -4673,7 +4825,8 @@ \subsection{Traversals for nested recursive types} because the recursive use of $\text{PT}$ contains a nontrivial type expression ($A\times A$) instead of just $A$. To express the type equation~(\ref{eq:perfect-shaped-binary-tree-type-equation}) via -a recursion scheme, we introduce an additional functor $P$ and write: +a recursion scheme, we need to introduce an additional functor $P$ +and write: \[ \text{PT}^{A}\triangleq S^{A,\text{PT}^{P^{A}}}\quad,\quad\quad P^{A}\triangleq A\times A\quad. \] diff --git a/sofp-src/tex/sofp-typeclasses.tex b/sofp-src/tex/sofp-typeclasses.tex index 7652e7769..bee0f0aa9 100644 --- a/sofp-src/tex/sofp-typeclasses.tex +++ b/sofp-src/tex/sofp-typeclasses.tex @@ -195,15 +195,17 @@ \subsection{Partial functions of types and values} does not throw exceptions. Total functions are safer to use than partial functions. The partial -function \lstinline!p! can be converted into a total function by -changing its type to \lstinline!Left[Int, String] => Int!. Another -example: applying \lstinline!head! to a \lstinline!List! is unsafe, -but the type \lstinline!NonEmptyList! guarantees at compile time -that the first element exists: +function \lstinline!p! can be made into a total function by changing +its type to \lstinline!Left[Int, String] => Int!. + +Another example: applying \lstinline!head! to a \lstinline!List! +is unsafe, but the type \lstinline!NonEmptyList! guarantees that +the first element exists: \begin{lstlisting} val xs: NonEmptyList[Int] = ... val h = xs.head // _.head is a total function for a NonEmptyList. \end{lstlisting} + In these cases, we achieve safety by making types more constrained. Similarly, partial type-to-value functions (PTVFs) become safe to use if we impose suitable typeclass constraints on the type parameters. @@ -274,17 +276,16 @@ \subsection{Creating a partial type-to-value function (PTVF)} \begin{lstlisting} def avg[T](s: Seq[T], frac: Frac[T]): T \end{lstlisting} -The value \lstinline!frac: Frac[T]! is called a \index{typeclass!instance value}\textbf{typeclass -instance} value. Because that value needs to be passed to every call -of \lstinline!avg[T]!, we will be unable to use types \lstinline!T! -for which \lstinline!Frac[T]! is void (i.e., has no values). +We will be unable to use \lstinline!avg[T]! with types \lstinline!T! +for which we have no values of type \lstinline!Frac[T]! because such +a value needs to be passed to every call to \lstinline!avg[T]!. Such +values are called \textsf{``}typeclass instance values\textsf{''} or, for brevity, +just \textbf{typeclass instances}.\index{typeclass!instance value} -A typeclass instance argument such as \lstinline!frac: Frac[T]! is +A typeclass instance argument like \lstinline!frac: Frac[T]! is also called an \textbf{evidence parameter}\index{evidence value} \index{typeclass!evidence parameter} because it \textsf{``}provides evidence\textsf{''} that the type \lstinline!T! belongs -to the type domain of the typeclass. Such evidence values are called -\textsf{``}typeclass instance values\textsf{''} or, for brevity, just \textsf{``}typeclass -instances\textsf{''}. +to the type domain of the typeclass. In this way, we implemented the typeclass constraint for the PTVF \lstinline!avg[T]!. The main steps were: @@ -336,7 +337,7 @@ \subsection{Creating a partial type-to-value function (PTVF)} \begin{lstlisting} def avg[T](s: Seq[T], frac: Frac[T]): T = { // Assuming `s` is non-empty. val sum = s.reduce(frac.add) // `s.reduce` fails if `s` is empty! - frac.intdiv(sum, s.length) // Compute `sum/length`. + frac.intdiv(sum, s.length) // Compute `sum/s.length`. } \end{lstlisting} To use this function, we need to pass a typeclass instance for the @@ -377,12 +378,11 @@ \subsection{Creating a partial type-to-value function (PTVF)} The function \lstinline!avg[T]! works unchanged with this implementation of \lstinline!Frac!. -The \lstinline!trait!-based code is significantly longer than the -code based on a case class. One advantage of the longer code is the -ability to combine typeclasses by inheriting from \lstinline!trait!s. -We will look at that in more detail below. For now, we note that both -implementations will require the programmer to add a significant amount -of new code: +The \lstinline!trait!-based code is longer than the code based on +a case class. One advantage of the longer code is the ability to combine +typeclasses by inheriting from \lstinline!trait!s. We will look at +that in more detail below. For now, we note that both implementations +require the programmer to add a significant amount of new code: \begin{itemize} \item Calls to \lstinline!func[T](args)! need to be changed to \lstinline!func[T](args, ti)! with typeclass instances \lstinline!ti!. @@ -397,30 +397,31 @@ \subsection{Creating a partial type-to-value function (PTVF)} \subsection{Scala\textsf{'}s \texttt{implicit} values} An \textbf{implicit }\index{implicit value} declaration is a feature -of Scala that makes arguments automatically available to functions -that declare \textsf{``}implicit arguments\textsf{''}. +of Scala that passes arguments automatically to functions that declare +\textsf{``}implicit arguments\textsf{''}. Scala\textsf{'}s syntax for implicit values is: \begin{lstlisting} implicit val x: Int = 123 \end{lstlisting} This declaration introduces an implicit value of type \lstinline!Int! -into the current scope. That value will be automatically passed as -an argument to any function declaring an argument of type \lstinline!Int! -as \lstinline!implicit!: +into the current scope. That value will be automatically passed to +any function declaring an argument of type \lstinline!Int! as \lstinline!implicit!: \begin{lstlisting} def f(a: String)(implicit n: Int) = s"$a with $n" scala> f("xyz") res0: String = xyz with 123 \end{lstlisting} -We need to declare the arguments as \lstinline!implicit! in the function\textsf{'}s -type signature, and the implicit arguments must be in a \emph{separate} -\index{argument list}argument list. +We need to declare the implicit arguments using the Scala keyword +\lstinline!implicit! (in Scala 3, the keyword \textsf{``}\lstinline!given!\textsf{''}). +All the implicit arguments must be in a \emph{separate} \index{argument list}argument +list in the function\textsf{'}s type signature. The simplest useful function with an implicit argument is the identity -function. In Scala 2, this function is called \lstinline!implicitly!. -Compare its code with the code of the ordinary identity function: +function. In Scala 2, this function is called \lstinline!implicitly!; +in Scala 3, it is called \lstinline!summon!. Compare its code with +the code of the ordinary identity function: \begin{lstlisting} def identity[T](t: T): T = t def implicitly[T](implicit t: T): T = t // An identity function with an implicit argument. @@ -498,7 +499,7 @@ \subsection{Implementing typeclasses by making instances \texttt{implicit} } \begin{lstlisting} def avg[T](s: Seq[T])(implicit frac: Frac[T]): T = { val sum = s.reduce(frac.add) - frac.intdiv(sum, s.length) // Compute `sum/length`. + frac.intdiv(sum, s.length) } \end{lstlisting} It is now easier to use the function \lstinline!avg! because evidence @@ -576,7 +577,7 @@ \subsection{Extension methods} \end{enumerate} The last two syntax features are often used when writing chains of function applications, such as \lstinline!xs.map(f).filter(g)!, because -that code is easier to read than \lstinline!filter(map(xs, f), g)!. +that code is easier to work with than \lstinline!filter(map(xs, f), g)!. The method syntax is available only for methods defined in a class. A special feature of Scala allows us to add new functions with method @@ -623,19 +624,18 @@ \subsection{Extension methods} new AvgSyntax(Seq(1.0, 2.0, 3.0))(implicitly[Frac[Double]]).average \end{lstlisting} In this way, the method \lstinline!average! is actually invoked on -a temporarily created value of type \lstinline!AvgSyntax!. These -values will be created automatically because the class constructor -of \lstinline!AvgSyntax! is declared as \lstinline!implicit!. Since -the constructor of \lstinline!AvgSyntax! includes the typeclass constraint -\lstinline![T: Frac]!, we will not be able to create values of type -\lstinline!AvgSyntax[T]! for types \lstinline!T! not in the type -domain of \lstinline!Frac!. +a temporarily created value of type \lstinline!AvgSyntax!. That value +will be created automatically because the class constructor of \lstinline!AvgSyntax! +is declared as \lstinline!implicit!. Since the constructor of \lstinline!AvgSyntax! +includes the typeclass constraint \lstinline![T: Frac]!, we will +not be able to create values of type \lstinline!AvgSyntax[T]! for +types \lstinline!T! not in the type domain of \lstinline!Frac!. This example illustrates the convenience of implementing PTVFs as extension methods. An extension method is defined only once but automatically becomes available for all types in the domain of the typeclass. Because of the typeclass constraint, the new method will be available \emph{only} -on values of supported types. +for supported types. This convenience comes at a cost: helper classes such as \lstinline!AvgSyntax! need to be explicitly imported into every scope where extension methods @@ -948,8 +948,8 @@ \subsubsection{Example \label{subsec:tc-Example-Semigroups}\ref{subsec:tc-Exampl Due to the associativity law~(\ref{eq:associativity-law-semigroup}), the result of \lstinline!x |+| y |+| z! does not depend on the choice of parentheses: \lstinline!(x |+| y) |+| z == x |+| (y |+| z)!. This -makes programs written using the semigroup operation \lstinline!|+|! -easier to understand and reason about. +makes code involving the semigroup operation (\lstinline!|+|!) easier +to read and to reason about. Semigroup types represent data that can be pairwise \textsf{``}merged\textsf{''} in a certain well-defined way. Using the \lstinline!Semigroup! typeclass, @@ -999,7 +999,7 @@ \subsubsection{Example \label{subsec:tc-Example-Semigroups}\ref{subsec:tc-Exampl in their original order. It is evident that the concatenation \lstinline!x ++ (y ++ z)! is a list with the same elements in the same order. However, a rigorous proof of the associativity law for lists, starting from the code of -the \lstinline!concat! function, requires significant work (see Section~\ref{subsec:Proofs-for-associativity-law-lists-and-arrays-concat}). +the \lstinline!concat! function, requires some work (see Section~\ref{subsec:Proofs-for-associativity-law-lists-and-arrays-concat}). \subsubsection{Example \label{subsec:tc-Example-semigroup-alternative-implementations}\ref{subsec:tc-Example-semigroup-alternative-implementations} (alternative semigroup implementations)} @@ -1052,14 +1052,14 @@ \subsubsection{Example \label{subsec:tc-Example-semigroup-alternative-implementa \left(\left(a_{1}\times b_{1}\right)\oplus\left(a_{2}\times b_{2}\right)\right)\oplus\left(a_{3}\times b_{3}\right) & =\left(a_{1}\times b_{2}\right)\oplus\left(a_{3}\times b_{3}\right)=a_{1}\times b_{3}\quad,\\ \left(a_{1}\times b_{1}\right)\oplus\left(\left(a_{2}\times b_{2}\right)\oplus\left(a_{3}\times b_{3}\right)\right) & =\left(a_{1}\times b_{1}\right)\oplus\left(a_{2}\times b_{3}\right)=a_{1}\times b_{3}\quad. \end{align*} -The implementation is possible for any types $A$, $B$: +The implementation works for any types $A$, $B$: \begin{lstlisting} implicit def semigroup2[A, B] = Semigroup[(A, B)]{ case ((a1, b1), (a2, b2)) => (a1, b2) } \end{lstlisting} -One use case for this semigroup is to maintain a pair of timestamps +One use case for this semigroup is for maintaining a pair of timestamps for the first and the last events in a temporally ordered series. -Merging two such pairs for consecutive events means to keep the first +Merging two such pairs for consecutive events will keep the first value from the first pair and the second value from the second pair. \textbf{(c)} It is clear that $x\oplus y\oplus z$ is the longest @@ -1090,8 +1090,8 @@ \subsubsection{Example \label{subsec:tc-Example-Monoids}\ref{subsec:tc-Example-M operation. Combining the \textsf{``}empty\textsf{''} value with another value will not change that value. For instance, concatenating with an empty list does not change a given list; so, the empty list is the \textsf{``}empty\textsf{''} -value for lists. Adding zero to an integer does not change that integer; -so, zero is the \textsf{``}empty\textsf{''} value when adding integers. +value for the list concatenation. Adding zero to an integer does not +change that integer; so, zero is the \textsf{``}empty\textsf{''} value for the addition. A semigroup with a designated \textsf{``}empty\textsf{''} value is called a \textbf{monoid}\index{monoid|textit}. Formally, a type $T$ is a monoid when there is an associative binary @@ -1183,7 +1183,7 @@ \subsubsection{Example \label{subsec:tc-Example-Monoids-1}\ref{subsec:tc-Example law\index{associativity law!of monoids} will hold for the monoid if the \lstinline!Semigroup! typeclass instance was already lawful. However, the value stored in the \lstinline!HasDefault! instance -is not guaranteed to satisfy the identity laws~(\ref{eq:identity-laws-of-monoid}) +will not necessarily obey the identity laws~(\ref{eq:identity-laws-of-monoid}) with respect to the \lstinline!combine! operation stored in the \lstinline!Semigroup! instance. The programmer must verify that the identity laws hold. It can happen that, for some type $T$, typeclass instances of \lstinline!Semigroup! @@ -1193,7 +1193,6 @@ \subsubsection{Example \label{subsec:tc-Example-Monoids-1}\ref{subsec:tc-Example instances will not work, and a different \lstinline!Monoid! instance must be defined. - \subsection{Typeclasses for type constructors\label{subsec:Typeclasses-for-type-constructors}} As an example of a function parameterized by a type constructor, consider @@ -1216,32 +1215,30 @@ \subsection{Typeclasses for type constructors\label{subsec:Typeclasses-for-type- \text{map}:\forall(A,B).\,F^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow F^{B}\quad. \] So, a typeclass instance of the \lstinline!Functor! typeclass must -contain this function as a value. But defining the typeclass as before -via a \lstinline!case class! does not work with Scala 2: +contain this function as a value. Scala 3 directly supports an argument +type that \emph{itself} contains type quantifiers: \begin{lstlisting}[mathescape=true] -final case class Functor[F[_]](map: $\forall(A,B).\,$F[A] => (A => B) => F[B]) // Not possible in Scala 2. +final case class Functor[F[_]](map: [A] => [B] => F[A] => (A => B) => F[B]) \end{lstlisting} -Scala 3 directly supports an argument type that \emph{itself} contains -type quantifiers such as $\forall(A,B)$. In Scala 2, we have to replace -nested type quantifiers by a \lstinline!trait! with a \lstinline!def! -method:\index{typeclass!Functor@\texttt{Functor}} +In Scala 2, we have to use a \lstinline!trait! with a \lstinline!def! +method like this:\index{typeclass!Functor@\texttt{Functor}} \begin{lstlisting} trait Functor[F[_]] { def map[A, B](fa: F[A])(f: A => B): F[B] } \end{lstlisting} The type constructor \lstinline!Functor! has the type parameter \lstinline!F[_]!, -which is a type constructor. For any type constructor \lstinline!F!, -a value of type \lstinline!Functor[F]! is a wrapper for a value of -type $\forall(A,B).\,F^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow F^{B}$. -Values of type \lstinline!Functor! (i.e., typeclass instances) are -implemented with the \textsf{``}\lstinline!new { ... }!\textsf{''} syntax: +indicating that \lstinline!F! is itself a type constructor. A value +of type \lstinline!Functor[F]! is a wrapper for a value of type $\forall(A,B).\,F^{A}\rightarrow\left(A\rightarrow B\right)\rightarrow F^{B}$. +Values of type \lstinline!Functor[F]! (i.e., typeclass instances) +are implemented with the \textsf{``}\lstinline!new { ... }!\textsf{''} syntax: \begin{lstlisting} implicit val functorSeq = new Functor[Seq] { def map[A, B](fa: Seq[A])(f: A => B): Seq[B] = fa.map(f) } \end{lstlisting} -This is currently the most common way of defining typeclasses in Scala. +This is currently the most common way of defining such typeclasses +in Scala. It is convenient to declare \lstinline!map! as an extension method on the \lstinline!Functor! type constructors: @@ -1251,7 +1248,7 @@ \subsection{Typeclasses for type constructors\label{subsec:Typeclasses-for-type- } \end{lstlisting} If this class definition is in scope, the \lstinline!map! method -becomes available for values of functor types. +becomes available for values of functor-wrapped types. Using the \lstinline!Functor! typeclass and the syntax helper, we can now implement the function \lstinline!inject!: @@ -1267,10 +1264,10 @@ \subsection{Typeclasses for type constructors\label{subsec:Typeclasses-for-type- It is the programmer\textsf{'}s responsibility to verify that the laws hold. One way of checking the laws is to use the \texttt{scalacheck} library\index{scalacheck library@\texttt{scalacheck} library}\index{verifying laws with scalacheck@verifying laws with \texttt{scalacheck}}\footnote{\texttt{\href{https://www.scalacheck.org}{https://www.scalacheck.org}}} -that automatically runs random tests for the given assertions, trying -to discover a set of values for which some assertion fails. Using -the \lstinline!Functor! typeclass constraint, we can implement a -function (in our terminology, a PTVF) that checks the functor laws +that automatically runs random tests for the given properties, trying +to discover a set of values for which some property fails to hold. +Using the \lstinline!Functor! typeclass constraint, we can implement +a function (in our terminology, a PTVF) that checks the functor laws for \emph{any} given type constructor \lstinline!F!: \begin{lstlisting} import org.scalacheck.Arbitrary // Necessary imports and definitions. @@ -1296,15 +1293,105 @@ \subsection{Typeclasses for type constructors\label{subsec:Typeclasses-for-type- proof that the laws hold. One of the main themes of this book is to show how to perform such symbolic derivations. +\subsection{Non-extension methods of typeclasses} + +Most typeclasses contain methods that consume the values of a type +that is constrained to belong to that typeclass. As shown in Example~\ref{subsec:tc-Example-metadata-extractors}, +if a type \lstinline!T! belongs to the typeclass \lstinline!HasMetadata! +then the method \lstinline!getName: T => String! will consume a value +of type \lstinline!T!. In those cases, it is convenient to define +typeclass methods as extension methods and write code as \lstinline!t.getName! +for values \lstinline!t: T!. + +In some cases, however, a typeclass will contain methods that do \emph{not} +consume values of the types \lstinline!T! that belong to the typeclass. +We will call them \textsf{``}non-extension methods\index{\textsf{``}non-extension methods}\textsf{''}. + +Consider Example~\ref{subsec:tc-Example-Pointed-type} showing the +typeclass \lstinline!HasDefault! with the method \lstinline!value! +that \emph{returns} values of type \lstinline!T!. To access the functionality +of that typeclass, we need to write code such as \lstinline!implicitly[HasDefault[T]].value!. +The method \textsf{``}\lstinline!value!\textsf{''} is not defined as an extension +method on \lstinline!T!. + +Another such case is the \lstinline!empty! method of the \lstinline!Monoid! +typeclass shown in Example~\ref{subsec:tc-Example-Monoids}. In programs +that use the \lstinline!Monoid! typeclass, we could write \lstinline!implicitly[Monoid[T]].empty! +when we need to access that method. + +What if we define \lstinline!empty! as an extension method on a value +\lstinline!t: T!? +\begin{lstlisting} +implicit class MonoidEmpty[T: Monoid](t: T) { + def empty: T = implicitly[Monoid[T]].empty +} +\end{lstlisting} +This code works but is not helpful. We will be able to write \lstinline!t.empty! +only if we already have a value \lstinline!t! of a monoidal type +\lstinline!T!. However, the importance of a monoid\textsf{'}s \lstinline!empty! +method is that it can provide a value of the monoidal type \lstinline!T! +in a program scope where no other values of type \lstinline!T! are +available. Consider this code: +\begin{lstlisting}[numbers=left] +def toMonoid[T: Monoid]: Option[T] => T = { + case None => implicitly[Monoid[T]].empty + case Some(t) => t +} +\end{lstlisting} +In the scope of line 2, the code has no given values of type \lstinline!T!. +The \lstinline!empty! method is the only way of producing such values. + +To use non-extension methods of typeclasses more easily, Scala libraries +often implement helper methods in a companion object of the typeclass. +We will now show two ways of doing that for the \lstinline!Monoid! +typeclass. + +The first option is to define \lstinline!empty[T]! as a method in +the companion object: +\begin{lstlisting} +object Monoid { + def empty[T: Monoid]: T = implicitly[Monoid[T]].empty +} +\end{lstlisting} +Then we may write \lstinline!Monoid.empty[T]! instead of \lstinline!implicitly[Monoid[T]].empty!. + +A disadvantage of this approach is that the companion object will +need to implement one helper method for each of the non-extension +methods of a typeclass. This is not an issue for \lstinline!Monoid! +since there is only one non-extension method. + +The second option is to define a helper method that fetches the typeclass +evidence value from the implicit scope: +\begin{lstlisting} +object Monoid { + def apply[T: Monoid]: Monoid[T] = implicitly[Monoid[T]] +} +\end{lstlisting} +Then we may write \lstinline!Monoid[T].empty! instead of \lstinline!implicitly[Monoid[T]].empty!. +This approach works for typeclasses with any number of non-extension +methods. As an example, let us re-implement the function \lstinline!toMonoid! +using this technique: +\begin{lstlisting} +def toMonoid[T: Monoid]: Option[T] => T = { + case None => Monoid[T].empty + case Some(t) => t +} +\end{lstlisting} +This is the style preferred by many Scala libraries. + +The code examples in this book will assume that typeclasses define +non-extension methods in their companion objects in some way. + \section{Deriving typeclass instances via structural analysis} In Chapter~\ref{chap:Functors,-contrafunctors,-and} we analyzed -the structure of functors by checking which of the six standard type -constructions can make new functors out of previous ones. We will -now apply the same\index{structural analysis} \index{types!structural analysis}\textbf{structural -analysis} to various typeclasses. Is a product of two monoids a monoid? -Is a co-product of two semigroups a semigroup? Answers to such questions -will enable us to: +the structure of functors and contrafunctors by checking which of +the six standard type constructions can make new functors or contrafunctors +out of previous ones. We will now apply the same\index{structural analysis} +\index{types!structural analysis}\textbf{structural analysis} to +various typeclasses. Is a product of two monoids a monoid? Is a co-product +of two semigroups a semigroup? Answers to such questions will enable +us to: \begin{itemize} \item Quickly decide whether a given type can have a typeclass instance of \lstinline!Monoid!, \lstinline!Semigroup!, etc. @@ -1386,9 +1473,9 @@ \subsection{Extractors} \] Both implementations give a valid \lstinline!Extractor! instance (since there are no laws to check). However, every choice will use -one of the two \lstinline!Extractor! instances and ignore the other. -So, we can simplify this construction by keeping the typeclass constraint -only for $A$ and allowing \emph{any} type $B$: +only one of the two \lstinline!Extractor! instances and ignore the +other. So, we can simplify this construction by keeping the typeclass +constraint only for $A$ and allowing \emph{any} type $B$: \[ \text{extractorPair}:\forall(A,B,Z).\,\text{Extractor}^{A}\rightarrow\text{Extractor}^{A\times B}\quad. \] @@ -1420,12 +1507,12 @@ \subsection{Extractors} \] \begin{lstlisting} def extractorEither[Z, A, B](implicit ti1: Extractor[Z, A], ti2: Extractor[Z, B]) = - Extractor[Z, Either[A, B]] { - case Left(a) => ti1.extract(a) - case Right(b) => ti2.extract(b) -} + Extractor[Z, Either[A, B]] { + case Left(a) => ti1.extract(a) + case Right(b) => ti2.extract(b) + } \end{lstlisting} -So, the co-product of $A$ and $B$ can be given a unique \lstinline!Extractor! +So, the co-product of $A$ and $B$ has a unique \lstinline!Extractor! instance. Since the product and the co-product constructions preserve \lstinline!Extractor! @@ -1531,8 +1618,7 @@ \subsection{Extractors} as $T\triangleq S^{T}$, where $S$ is a type constructor built up by composing $F_{1}$, $F_{2}$, and/or $F_{3}$ in some way. (The type constructor $S$ may use $Z$ or other fixed types.) For each -of $F_{1}$, $F_{2}$, and/or $F_{3}$, we implemented a function -of type: +$F=F_{i}$ (with $i=1,2,3$), we implemented a function of type: \[ \text{extractorF}:\text{Extractor}^{A}\rightarrow\text{Extractor}^{F^{A}}\quad. \] @@ -1547,8 +1633,8 @@ \subsection{Extractors} x^{:T\rightarrow Z}\triangleq\text{extractorS}\left(x\right)\quad. \] The types match because the type $T$ is equivalent to the type $S^{T}$. -As long as the definition of the recursive type $T$ is valid (i.e., -the type recursion terminates), the extractor function will also terminate. +As long as the recursive type $T$ is not void (i.e., the type recursion +terminates), the extractor function will also terminate. To illustrate this construction, let us derive an \lstinline!Extractor! instance for the type $T$ defined by Eq.~(\ref{eq:example-good-recursive-equation-extractor}). @@ -1581,16 +1667,17 @@ \subsection{Extractors} \end{array}\quad. \] \begin{lstlisting} -def extractorS[A](f: Extractor[A]): Extractor[S[A]] = Extractor[S[A]] { case (c, g) => - g(c) match { - case Left(z) => z - case Right((_, t)) => f.extract(t) +def extractorS[A](f: Extractor[A]): Extractor[S[A]] = Extractor[S[A]] { + case (c, g) => + g(c) match { + case Left(z) => z + case Right((_, t)) => f.extract(t) + } } -} \end{lstlisting} The recursive construction defines an \lstinline!Extractor! instance -for $T$ by a recursive equation: +for $T$ by: \begin{equation} \text{extractorT}\triangleq\text{extractorS}\left(\text{extractorT}\right)\quad.\label{eq:recursive-extractor-def} \end{equation} @@ -1611,9 +1698,10 @@ \subsection{Extractors} \paragraph{Why the recursion terminates} -The above code shows that the recursive definition~(\ref{eq:recursive-extractor-def}) -terminates. Why does it? A recursive definition of the form $x\triangleq f(x)$ -could create an infinite loop, as in this code: +The test code above shows that the recursive definition~(\ref{eq:recursive-extractor-def}) +terminates. Why does it, though? A recursive \emph{value} definition +of the form $x\triangleq f(x)$ could create an infinite loop, as +in this code: \begin{lstlisting} def f(i: Int): Int = i + 1 def x: Int = f(x) @@ -1631,15 +1719,15 @@ \subsection{Extractors} \begin{lstlisting} def extractorT: Extractor[TypeT] = Extractor { case TypeT(t) => extractorS(extractorT).extract(t) } \end{lstlisting} -Although a function $f$ is equivalent to its \textbf{expanded form} -\index{expanded form of a function!use in recursive values} $t\rightarrow f(t)$, -there is an important difference: using expanded forms in the code -will make recursive definitions \index{recursive function!termination} -terminate. +Although a function $f$ and its \textbf{expanded form} \index{expanded form of a function!use in recursive values} +$t\rightarrow f(t)$ are equivalent, there is an important difference: +writing expanded forms of functions will make recursive value definitions +\index{recursive function!termination} terminate. To see an example of that, consider a recursive equation $f\triangleq k(f)$, -where $k$ is some function. The Scala code \lstinline!val f = k(f)! -creates an infinite loop when we compute anything that involves $f$: +where $k$ is some function. The Scala code \lstinline!def f = k(f)! +creates an infinite loop whenever we compute anything that involves +$f$: \begin{lstlisting}[mathescape=true] def k(f: Int => Int): Int => Int = { x => if (x <= 0) 1 else 2 * f(x - 1) } def f: Int => Int = k(f) // This definition is invalid! @@ -1650,13 +1738,12 @@ \subsection{Extractors} scala> f(4) // Infinite loop: k(k(k(k(...)))(4) java.lang.StackOverflowError: ... \end{lstlisting} -This code is clearly invalid. But if we expand the right-hand side -of the recursive equation to: +The code $f\triangleq k(f)$ is clearly invalid. But if we expand +the right-hand side of this recursive definition to: \[ -f\triangleq t\rightarrow k(f)(t) +f\triangleq t\rightarrow k(f)(t)\quad, \] -instead of $f\triangleq k(f)$, the code will become valid, and the -infinite loop disappears: +instead of $f\triangleq k(f)$, the infinite loop disappears: \begin{lstlisting}[mathescape=true] def f: Int => Int = { x => k(f)(x) } // This defines $\color{dkgreen} f(n) = 2^n $ for $\color{dkgreen}n \geq 0$. @@ -1687,10 +1774,13 @@ \subsection{Extractors} implicit def extractorK[Z,P,Q,R,S]: Extractor[Z, K[Z,P,Q,R,S]] = { implicit val e1 = extractorPair[Z, Z, S] // $\color{dkgreen} Z \times S $. Needs Extractor[Z, Z]. implicit val e2 = extractorFunc[Z, (Z, S), R] // $\color{dkgreen} R\times\left(R\rightarrow Z\times S\right) $. - implicit val e3 = extractorEither[Z, Z, (R, R => (Z, S))] // $\color{dkgreen} Z+R\times\left(R\rightarrow Z\times S\right) $. - implicit val e4 = extractorFunc[Z, Either[Z, (R, R => (Z, S))], Q] // $\color{dkgreen} Q\times\left(Q\rightarrow Z+R\times\left(R\rightarrow Z\times S\right)\right) $. + // $\color{dkgreen} Z+R\times\left(R\rightarrow Z\times S\right) $. + implicit val e3 = extractorEither[Z, Z, (R, R => (Z, S))] + // $\color{dkgreen} Q\times\left(Q\rightarrow Z+R\times\left(R\rightarrow Z\times S\right)\right) $. + implicit val e4 = extractorFunc[Z, Either[Z, (R, R => (Z, S))], Q] implicit val e5 = extractorPair[Z, Z, P] // $\color{dkgreen} Z\times P $. - extractorEither[Z, (Z,P), (Q, Q => Either[Z, (R, R => (Z, S))])] // Extractor for type $\color{dkgreen} K $. + // Extractor for type $\color{dkgreen} K $. + extractorEither[Z, (Z,P), (Q, Q => Either[Z, (R, R => (Z, S))])] } \end{lstlisting} The code computes each implicit value \lstinline!e1!, \lstinline!e2!, @@ -1698,9 +1788,9 @@ \subsection{Extractors} be present as implicits. The \lstinline!Extractor! typeclass is often used with \lstinline!Z = String! -as a way to \textsf{``}print\textsf{''} values of different types, and it is then -called \lstinline!Show!\index{typeclass!Show@\texttt{Show}}. When -\lstinline!Z = Array[Byte]!, the typeclass is often called a \textsf{``}serializer\index{serializer}\textsf{''}. +as a way to print values of different types, and it is then called +\lstinline!Show!\index{typeclass!Show@\texttt{Show}}. When \lstinline!Z = Array[Byte]!, +the typeclass is often called a \textsf{``}serializer\index{serializer}\textsf{''}. \begin{table} \begin{centering} @@ -1733,7 +1823,7 @@ \subsection{Equality comparison: The \texttt{Eq} typeclass\label{subsec:The-Eq-t In Scala, the built-in operation \lstinline!==! is not type-safe because the code \lstinline!x == y! will compile regardless of the types of \lstinline!x! and \lstinline!y!. We can replace \lstinline!==! -by a new operation \lstinline!===! constrained to a typeclass called +by a new operation (\lstinline!===!) constrained to a typeclass called \lstinline!Eq!, ensuring that types can be meaningfully compared for equality. The equality comparison for values of type $A$ is a function of type $A\times A\rightarrow\bbnum 2$ (where $\bbnum 2$ @@ -4559,7 +4649,7 @@ \subsection{Exercises\index{exercises}} \subsubsection{Exercise \label{subsec:tc-Exercise-1}\ref{subsec:tc-Exercise-1}} -Define a PTVF \lstinline!def isLong[T]: Boolean! that returns \lstinline!true! +Define a PTVF \lstinline!isLong[T]: Boolean! that returns \lstinline!true! for \lstinline!T = Long! or \lstinline!Double! and returns \lstinline!false! for \lstinline!T = Int!, \lstinline!Short!, or \lstinline!Float!. The function should remain undefined for other types \lstinline!T!. @@ -4667,8 +4757,8 @@ \subsubsection{Exercise \label{subsec:tc-Exercise-9-1-1}\ref{subsec:tc-Exercise- \subsubsection{Exercise \label{subsec:tc-Exercise-9-1-1-1}\ref{subsec:tc-Exercise-9-1-1-1}} -(dual O\textsf{'}Connor\index{Russell O\textsf{'}Connor}) Assume that a functor $F$ -admits a function $q$ with type signature: +\footnote{This property is dual to O\textsf{'}Connor\textsf{'}s pointed functor property\index{Russell O\textsf{'}Connor}; +see Example~\ref{subsec:tc-Example-pointed-alternative}.} Assume that a functor $F$ admits a function $q$ with type signature: \begin{lstlisting} def q[A, B, F[_]: Functor]: F[(A, B)] => (A, F[B]) \end{lstlisting} diff --git a/sofp-src/tex/sofp.tex b/sofp-src/tex/sofp.tex index 31aa31553..10b9cafee 100644 --- a/sofp-src/tex/sofp.tex +++ b/sofp-src/tex/sofp.tex @@ -50,6 +50,22 @@ \fontencoding{T2A}\selectfont\def\encodingdefault{T2A}} \DeclareRobustCommand{\textcyr}[1]{\leavevmode{\cyrtext #1}} +%% Special footnote code from the package 'stblftnt.sty' +%% Author: Robin Fairbairns -- Last revised Dec 13 1996 +\let\SF@@footnote\footnote +\def\footnote{\ifx\protect\@typeset@protect + \expandafter\SF@@footnote + \else + \expandafter\SF@gobble@opt + \fi +} +\expandafter\def\csname SF@gobble@opt \endcsname{\@ifnextchar[%] + \SF@gobble@twobracket + \@gobble +} +\edef\SF@gobble@opt{\noexpand\protect + \expandafter\noexpand\csname SF@gobble@opt \endcsname} +\def\SF@gobble@twobracket[#1]#2{} %% Because html converters don't know tabularnewline \providecommand{\tabularnewline}{\\} \RS@ifundefined{subsecref} @@ -320,9 +336,9 @@ {\footnotesize{}ISBN (e-book): 978-0-359-76877-6}\\ {\footnotesize{}ISBN: 978-0-359-76877-6}\\ \\ -{\scriptsize{}Source hash (sha256): ddc887c85af605b9dfc78384a9b367cca3e7ff5ac1dae8ac02fa0be8639c9f9a}\\ -{\scriptsize{}Git commit: 6567bd3d1d697221e6d321f0827dac34f79bc150}\\ -{\scriptsize{}PDF file built on Mon, 15 Jul 2024 14:50:11 +0200 by pdfTeX 3.141592653-2.6-1.40.25 (TeX Live 2023) on Darwin}\\ +{\scriptsize{}Source hash (sha256): b9278decd0827aca6ba71818d4490fe2d8925422dc014dbb986cc653419b959e}\\ +{\scriptsize{}Git commit: d02f9ffd623bbc82fc2f0c9ea98dc38f23851b64}\\ +{\scriptsize{}PDF file built on Thu, 08 Aug 2024 12:11:31 +0200 by pdfTeX 3.141592653-2.6-1.40.25 (TeX Live 2023) on Darwin}\\ ~\\ {\scriptsize{}Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, @@ -357,10 +373,10 @@ practical applications of parametricity.}\\ {\scriptsize{}}\\ {\scriptsize{}Long and difficult, yet boring explanations are logically -developed in excruciating detail through 1892 Scala -code snippets, 191 statements with step-by-step derivations, -103 diagrams, 223 examples with tested Scala -code, and 308 exercises. Discussions build upon each +developed in excruciating detail through 1905 Scala +code snippets, 192 statements with step-by-step derivations, +104 diagrams, 223 examples with tested Scala +code, and 310 exercises. Discussions build upon each chapter's material further.}\\ {\scriptsize{}}\\ {\scriptsize{}Beginners in FP will find tutorials about the map/reduce