aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ch6_monads.tex32
-rw-r--r--ch7_programming.tex640
-rw-r--r--main.tex4
-rw-r--r--ref.bib74
4 files changed, 734 insertions, 16 deletions
diff --git a/ch6_monads.tex b/ch6_monads.tex
index b07996c..a728df7 100644
--- a/ch6_monads.tex
+++ b/ch6_monads.tex
@@ -71,7 +71,7 @@ en \cite[VI]{maclane}.
\label{fig:monad}
\end{figure}
-\begin{example}\;
+\begin{example}\label{ex:monads}\;
\begin{enumerate}
\item $\power:\bSet\to\bSet$ es una mónada, descrita en la introducción del
capítulo.
@@ -96,20 +96,22 @@ en \cite[VI]{maclane}.
de) otros árboles y sustituiría las hojas por los subárboles que
representan. Es fácil ver que estas transformaciones son naturales y forman
una mónada.
- \item Sean $\cC$ una categoría con coproductos finitos y $d$ un objeto de
- $\cC$. Sea $E:\cC\to\cC$ un endofuntor que a cada objeto $c$ le asocia
- $c\oplus d$ y a cada morfismo $f:b\to c$ el morfismo <<suma>>
- $Ef=f\oplus 1_d:b\oplus d\to c\oplus d$. Si $\eta_c:c\monicTo c\oplus d$ es
- la inclusión canónica y $\mu_c:c\oplus d\oplus d\epicTo c\oplus d$ es la
- función que <<identifica>> las dos copias de $d$ en el sentido evidente,
- entonces $(E,\eta,\mu)$ es una mónada.
- \item Sean $s$ un conjunto y $S\coloneqq\hom(s,-\times s)$ un endofuntor en
- $\bSet$ que lleva cada conjunto $X$ al conjunto de funciones
- $s\to X\times s$ y cada función $f$ a $\hom(s,f\times 1_s)$. Entonces $S$ es
- una mónada con las transformaciones naturales $\eta:1\to S$ y $\mu:S^2\to S$
- dadas por la formación de pares $\eta_X(x)(y)\coloneqq(x,y)$ y la <<doble
- evaluación>> $\mu_X(f)(y)\coloneqq (p_1fy)(p_2fy)$, donde $p_1$ y $p_2$ son
- las proyecciones canónicas de $\hom(X,s)\times s$.
+ \item \label{enu:monad-result} Sean $\cC$ una categoría con coproductos
+ finitos y $d$ un objeto de $\cC$. Sea $E:\cC\to\cC$ un endofuntor que a cada
+ objeto $c$ le asocia $c\oplus d$ y a cada morfismo $f:b\to c$ el morfismo
+ <<suma>> $Ef=f\oplus 1_d:b\oplus d\to c\oplus d$. Si
+ $\eta_c:c\monicTo c\oplus d$ es la inclusión canónica y
+ $\mu_c:c\oplus d\oplus d\epicTo c\oplus d$ es la función que <<identifica>>
+ las dos copias de $d$ en el sentido evidente, entonces $(E,\eta,\mu)$ es una
+ mónada.
+ \item \label{enu:monad-state} Sean $s$ un conjunto y
+ $S\coloneqq\hom(s,-\times s)$ un endofuntor en $\bSet$ que lleva cada
+ conjunto $X$ al conjunto de funciones $s\to X\times s$ y cada función $f$ a
+ $\hom(s,f\times 1_s)$. Entonces $S$ es una mónada con las transformaciones
+ naturales $\eta:1\to S$ y $\mu:S^2\to S$ dadas por la formación de pares
+ $\eta_X(x)(y)\coloneqq(x,y)$ y la <<doble evaluación>>
+ $\mu_X(f)(y)\coloneqq (p_1fy)(p_2fy)$, donde $p_1$ y $p_2$ son las
+ proyecciones canónicas de $\hom(X,s)\times s$.
% \begin{proof}
% Escribiendo $e(f,y)\coloneqq f(y)$, se tiene $\mu_X(f)(y)\equiv e(f(y))$ y
% $\mu_X(f)\equiv e\circ f$ (por abuso de notación, $e$ no representa una
diff --git a/ch7_programming.tex b/ch7_programming.tex
new file mode 100644
index 0000000..0756a3f
--- /dev/null
+++ b/ch7_programming.tex
@@ -0,0 +1,640 @@
+Al nivel más básico, un programa es simplemente una serie de instrucciones a
+ejecutar por un ordenador. Sin embargo, estos programas suelen ser complejos y
+es común introducir fallos a la hora de crearlos, por lo que son necesarias
+herramientas para reducir el número de fallos y, en sistemas críticos, para
+demostrar su ausencia mediante pruebas de corrección.
+
+Para probar la corrección de un programa, primero debemos modelarlo
+matemáticamente, y para ello existen varias alternativas:
+
+\begin{description}
+\item[Semántica axiomática] Usa reglas lógicas para describir el efecto de los
+ comandos ejecutados sobre proposiciones lógicas, definiendo relaciones entre
+ las proposiciones que son verdad antes y después de su ejecución. Por ejemplo,
+ uno de los axiomas podría ser <<si $E$ es una expresión, $v$ es una variable y
+ $P$ es una propiedad, si se cumple $P(E)$, tras ejecutar $v\gets E$ se cumple
+ $P(v)$>>.
+\item[Semántica denotativa] Los programas se interpretan como descripciones de
+ objetos matemáticos en ciertos dominios, como funciones entre la entrada y la
+ salida.
+\item[Semántica operacional] El programa en sí se considera como un objeto
+ matemático, y las pruebas se construyen a partir de axiomas que definen su
+ comportamiento.
+\end{description}
+
+Tradicionalmente los programas se han escrito de forma imperativa, como
+secuencias de comandos que modifican el estado (los valores de las variables) y
+se comunican con los dispositivos de entrada y salida. Para estos programas es
+común usar la semántica axiomática, que encaja bien al estar también basada en
+comandos.
+
+Sin embargo, en general razonar sobre programas imperativos complejos es
+difícil, pues, por ejemplo, a la hora de razonar no siempre es válido sustituir
+una variable por la expresión que se le ha asignado, pues el valor de dicha
+expresión puede cambiar y, además, ejecutar la expresión puede modificar otras
+partes del estado del programa.
+
+Para facilitar el razonamiento surge la programación funcional. En esta los
+procedimientos actúan como funciones matemáticas, en el sentido de que no se
+comunican con el exterior y el valor devuelto depende exclusivamente de los
+valores de los parámetros de entrada. Además, los valores de las variables no se
+pueden modificar una vez se ha definido la variable, permitiendo sustituir
+libremente las variables por su expresión de forma relativamente sencilla.
+
+La semántica axiomática no es apropiada para la programación funcional, pero la
+semántica denotativa proporciona un modelo muy elegante y sencillo de usar.
+Este paradigma de programación no se creó sólo para facilitar demostraciones
+rigurosas sobre los programas, sino también para facilitar el razonamiento
+intuitivo y la verificación automática y de este modo reducir notablemente la
+cantidad de fallos que se introducen en el programa en primer lugar.
+
+La programación funcional no sólo tiene ventajas respecto a corrección, sino que
+también permite reducir notablemente el tamaño de los programas tratando las
+funciones como valores y operando sobre ellas. Para ello se usa una variedad de
+técnicas heredadas de la teoría de categorías, como funtores, transformaciones
+naturales y mónadas.
+
+El lector habrá notado que, si los procedimientos son funciones que no se
+comunican con el exterior, es imposible crear programas interactivos. Sin
+embargo, la idea no es definir el programa completo de forma funcional, sino
+reducir al mínimo las partes no funcionales. Respecto a la formalización, las
+partes no funcionales se pueden formalizar de otra forma más tradicional o
+mediante una construcción que involucra categorías de Kleisli, como veremos a
+continuación.
+
+\section{La categoría $\bType$}
+
+Por lo general, los tratamientos categóricos de la programación funcional se
+basan en una categoría $\bType$ cuyos objetos son tipos de datos y cuyos
+morfismos son funciones computables. La estructura concreta de $\bType$ depende
+del lenguaje de programación utilizado, por lo que primero definiremos un
+lenguaje de programación funcional sencillo que consideramos representativo.
+
+El lenguaje que definiremos no es especialmente potente o minimalista, aunque
+permite describir la mayoría de construcciones usadas en lenguajes de
+programación funcional sin tener que entrar en aspectos de lógica formal que, si
+bien son interesantes, se salen del ámbito de este trabajo.
+
+El lenguaje que vemos no permite construcciones avanzadas como pueden ser tratar
+una mónada como un valor; sin embargo, podemos hablar de mónadas de forma
+extrínseca. Para este tipo de construcciones necesitamos un lenguaje como el
+descrito en \cite{hindley-milner}, o incluso el descrito en \cite{coc}, que
+además puede usarse como fundamento para la lógica formal, aunque estos
+lenguajes no permiten identificar los tipos con su dominio de valores de forma
+directa.\cite{polynoset}
+
+Toda expresión $e$ válida en lenguaje tiene un tipo $T$ asociado, y escribimos
+$e:T$ para expresar esto. Además, todo tipo $T$ tiene un dominio $D_T$ asociado,
+y evaluar una expresión $e:T$ en un cierto contexto resulta en un valor de
+$D_T$. Los tipos que se pueden definir son:
+\begin{itemize}
+\item \conc{Tipos producto.} Si $t_1,\dots,t_k$ son tipos, $(t_1,\dots,t_k)$ es
+ un tipo con dominio $D_{t_1}\times\dots\times D_{t_k}$, y en particular existe
+ un \conc{tipo unidad} $()$ con un sólo elemento. Los tipos producto existen en
+ la mayoría de lenguaje de programación con el nombre de \emph{estructuras}.
+\item \conc{Tipos suma.} Si $t_1,\dots,t_k$ son tipos, $[t_1|\dots|t_k]$ es un
+ tipo cuyo dominio es la unión disjunta $D_{t_1}\oplus\dots\oplus D_{t_k}$, y
+ en particular existe un \conc{tipo vacío} $[]$ con dominio vacío. Los tipos
+ coproducto son poco comunes fuera de lenguajes de programación funcionales,
+ aunque se pueden aproximar mediante uniones en lenguajes de bajo nivel o
+ jerarquías de clases en lenguajes orientados a objetos.
+\item \conc{Tipos función.} Si $a$ y $b$ son tipos, $a\to b$ es un tipo cuyo
+ dominio son las funciones $D_a\to D_b$ que son \emph{computables}, que son
+ precisamente las que pueden ser expresadas en este lenguaje.
+\item \conc{Tipos sinónimo.} Una sentencia $\mathtt{type\ }T=t$, donde $T$ es un
+ identificador y $t$ es una expresión que representa un tipo, crea un tipo $T$
+ con el mismo dominio que $t$, y morfismos $T^*:t\to T$ y $T_*:T\to t$ que
+ actúan sobre los dominios como la identidad. Crucialmente, las sentencias de
+ este tipo pueden hacer referencia unas a otras y $t$ puede hacer referencia a
+ $T$, lo que permite definir tipos recursivos cuyo dominio es similar al de un
+ tipo suma con infinitas alternativas. Para que una alternativa esté habitada
+ debe tener un caso base; por ejemplo, la sentencia $\mathtt{type\ }T=T$ es
+ válida pero crea un tipo vacío.
+
+ Como extensión de esta sintaxis podemos sustituir $T$ por $T_{a_1,\dots,a_k}$,
+ que para tipos cualesquiera $\sigma_1,\dots,\sigma_k$ define
+ $T_{\sigma_1,\dots,\sigma_k}$ como sinónimo de
+ $t[\sigma_1/a_1]\cdots[\sigma_k/a_k]$.
+\end{itemize}
+
+Las expresiones del lenguaje son de la forma siguiente:
+\begin{itemize}
+\item En un contexto en que el identificador $x$ está vinculado a una expresión
+ de tipo $T$, $x$ es una expresión de tipo $T$ que representa el valor
+ vinculado al identificador en el contexto actual.
+\item Si $f:S\to T$ y $e:S$, entonces $fe$ es una expresión de tipo $T$ que
+ evalúa $f$ y $e$ y devuelve el resultado de aplicar la función dada por $f$ al
+ valor dado por $e$.
+\item Sean $x$ un identificador y $e$ una expresión que puede mencionar a $x$,
+ si cuando $x:S$ entonces $e:T$, la expresión $\lambda x:S.e$ es de tipo
+ $S\to T$ y representa una función que recibe un valor de tipo $x$ y
+ devuelve el valor de $e$ cuando $x$ está vinculado a dicho valor.
+\item Dadas expresiones $e_1:T_1,\dots,e_k:T_k$,
+ $(e_1,\dots,e_k):(T_1,\dots,T_k)$ representa el valor que en cada posición $i$
+ contiene el valor resultante de evaluar $e_i$, mientras que si
+ $e:(T_1,\dots,T_k)$ e $i\in\{1,\dots,k\}$, la expresión $e_i:T_i$ recupera el
+ valor en la componente $i$-ésima.
+\item Dada una expresión $e:T_i$, la expresión
+ $\langle e\rangle_i^{T_1,\dots,T_k}:[T_1|\dots|T_k]$ representa la inclusión
+ del valor de $e$ en la componente $i$-ésima de un tipo suma, mientras que si
+ $e:[T_1,\dots,T_k]$ y cada $x_i:T_i\to\gamma$,
+ $\mathtt{match\ }e\mathtt{\ \{}x_1\mathtt{;\ }\dots\mathtt{;\ }x_k\mathtt{\}}:\gamma$
+ es una expresión que evalúa $e$ y, según la componente
+ del tipo suma en que se encuentre el valor devuelto, ejecuta una de las
+ funciones con dicho valor y devuelve el valor devuelto por dicha función.
+\item Dadas expresiones $c:[(),()]$ y $t,e:T$, la expresión
+ $\mathtt{if\ }c\mathtt{\ then\ }t\mathtt{\ else\ }e$ equivale a
+ $(\mathtt{match\ }e\mathtt{\ \{}\lambda\_:().t\mathtt{;\ }\lambda\_:().e\mathtt{\}}()$.
+ Dicho de otro modo, esta función evalúa $c$ y, según el resultado, evalúa $t$
+ o $e$ y devuelve el resultado.
+\item Si, dados identificadores $x_1,\dots,x_k$ a los que le asignamos tipos
+ respectivos $T_1,\dots,T_k$, las expresiones $e_1,\dots,e_k,f$ tienen tipos
+ respectivos $T_1,\dots,T_k,S$, entonces
+ \[
+ \mathtt{let\ }x_1:T_1=e_1\mathtt{\ and\ }\dots\mathtt{\ and\ }x_k:T_k=e_k\mathtt{\ in\ }f
+ \]
+ es una expresión de tipo $S$ que evalúa cada $e_i$ y le asigna el valor al
+ correspondiente $x_i$, en orden, y finalmente evalúa $f$ y devuelve su
+ valor. Todas las evaluaciones se hacen con los $x_i$ vinculados a los
+ correspondientes valores, y es un error si evaluar uno de los $e_i$ requiere
+ evaluar un $x_j$ con $j\geq i$. Pese a ello, es posible hacer referencia a
+ dicho $x_j$ en $e_i$ si es dentro de una función, lo que permite definir
+ funciones recursivas.
+\end{itemize}
+
+En ocasiones omitiremos las declaraciones de tipos, escribiendo por ejemplo
+$\lambda x.e$ en vez de $\lambda x:T.e$. Además, escribiremos $x^*$ y $x_*$ en
+vez de $T^*x$ y $T_*x$ cuando quede claro de qué tipo se está hablando.
+
+Un programa está formado por una serie de sentencias que definen tipos sinónimo
+y sentencias que definen constantes, de la forma $\mathtt{const\ }x:T=e$ o,
+incluso, de la forma $\mathtt{const\ }x_{a_1,\dots,a_k}:T=e$, donde los $a_i$
+son variables de tipo para definir una constante por cada asignación de los
+tipos. Es importante destacar que estas constantes se definen de manera
+uniforme, es decir, la expresión $e$ y el tipo $T$ se definen igual para cada
+asignación de tipos $a_i$ y el resultado equivale a definir
+$\mathtt{const\ }x_{\sigma_1,\dots,\sigma_k}:T[\sigma_i/a_i]=e[\sigma_i/a_i]$
+para cada asignación de tipos $\sigma_1,\dots,\sigma_k$ donde los $\sigma_i$ no
+contienen ningún $a_j$ como variable libre.
+
+Las expresiones recursivas son particularmente importantes, pues equivalen a la
+iteración en programación iterativa. Por ejemplo, podemos obtener el último
+elemento de una lista como sigue:
+\begin{align*}
+ \texttt{type\ list}_a & = [() | (a, \texttt{list}_a)]\\
+ \texttt{const\ }\bot_a : a & = \texttt{let\ }f:()\to a=\lambda x:().fx\texttt{\ in\ }f()\\
+ \texttt{const last}_a : \texttt{list}_a \to a & = \lambda l.\texttt{match }l\ \{
+ \bot_{()\to a};\
+ \lambda c.\texttt{match }c_2\ \{\lambda x.c_1;\ \lambda c'.\texttt{last}_a c'\}
+ \}
+\end{align*}
+
+La primera línea define el tipo lista como la lista vacía o el par formado por
+el primer elemento de la lista y el resto de elementos. La segunda requiere ser
+analizada con más detalle. Esta define un elemento de cada tipo (¡incluso de los
+tipos con dominio vacío!) como el resultado de evaluar una expresión que nunca
+termina. El problema es que la recursividad introduce la posibilidad de crear
+expresiones que nunca terminan, pero a la vez es necesaria para no limitar el
+lenguaje.
+
+Para acomodar esto establecemos que el dominio de cada tipo tiene un (y sólo
+un\footnote{En lenguajes como Haskell puede haber varios valores similares a
+ $\bot$; por ejemplo, un elemento de tipo $(a,b)$ puede ser $\bot$, $(x,\bot)$,
+ $(\bot,y)$, etc. Esto se debe a que Haskell sólo evalúa las partes de las
+ expresiones que son necesarias y por tanto puede que no se llegue a evaluar la
+ parte que no termina. En nuestro caso el lenguaje es estricto y esto no
+ ocurre.}) elemento $\bot$ que representa cómputos que no terminan. Entonces,
+una evaluación $ef$ en que $e$ o $f$ es $\bot$ devuelve $\bot$;
+$(e_1,\dots,e_k)_i$ devuelve $\bot$ si y sólo si algún $e_i=\bot$,
+$\lambda x.\bot$ no devuelve $\bot$ (pero su aplicación sobre algún valor sí),
+$\langle\bot\rangle_i=\bot$, y una sentencia
+$\mathtt{match\ }e\mathtt{\ \{}x_1\mathtt{;\ }\dots\mathtt{;\ }x_k\mathtt{\ \}}$
+devuelve $\bot$ si y sólo si $e$ devuelve $\bot$ o $e=\langle e_0\rangle_i$ para
+cierto $e_0$ y $x_ie_0$ devuelve $\bot$.
+
+Finalmente, la tercera línea define la función buscada. Si la lista es vacía,
+devuelve el único valor que puede devolver, $\bot$, y de lo contrario comprueba
+si la lista tiene más de un elemento, devolviendo en tal caso el último elemento
+del resto de la lista y en otro caso el único elemento que hay. El uso de $\bot$
+para indicar un valor de retorno no definido es bastante habitual.
+
+Igual que hemos definido las listas, podemos definir los números naturales como
+$\mathtt{list}_{()}$ identificando cada número $n$ con la lista de longitud $n$,
+los booleanos como $[()|()]$, etc. Ya estamos en condiciones de definir
+$\bType$.
+
+\begin{definition}
+ La categoría $\bType$ (asociada a un programa) es aquella que tiene como
+ objetos los tipos que se pueden definir en el lenguaje usando los tipos
+ sinónimo definidos en el programa, como morfismos $a\to b$ los elementos de
+ $D_{a\to b}$, como composición de dos morfismos $f:a\to b$ y $g:b\to c$ el
+ morfismo dado por la expresión $\lambda x.g(fx)$, y como identidad el morfismo
+ $\lambda x.x$.
+\end{definition}
+\begin{align*}
+ \mathtt{const\ }(\circ)_{a,b,c}:(b\to c)\to(a\to b)\to(a\to c) & = \lambda x.g(fx)\\
+ \mathtt{const\ }1_a:a\to a & = \lambda x.x\\
+\end{align*}
+
+\section{Funtores}
+
+Podemos definir un funtor en nuestro lenguaje como un sinónimo de tipo de un
+parámetro $F_a$ junto con una familia de constantes
+$F_{a,b}:(a\to b)\to(F_a\to F_b)$, suponiendo que cumplan las condiciones
+necesarias. Esto nos da un endofuntor $\bType\to\bType$.
+
+\begin{example}
+ Algunos funtores comúnmente usados son los siguientes:
+ \begin{enumerate}
+ \item Las listas, definidas anteriormente, junto con la evaluación de funtores
+ componente a componente:
+ \begin{multline*}
+ \mathtt{const\ map}_{a,b}=
+ \lambda f:a\to b.\lambda l:\mathtt{list}_a.\,
+ \mathtt{match\ }l\ \{\lambda x.\langle()\rangle_1;\ \lambda c.\langle(fc_1,\mathtt{map}\,f\,c_2)\rangle_2\}
+ % \lambda f.\\\mathtt{let\ mapf}:\mathtt{list}_a\to\mathtt{list}_b=
+ % \lambda l.\mathtt{match}\ l\ \{\lambda x.\langle()\rangle_1;\ %
+ % \lambda c.\langle(fc_1,\mathtt{mapf}c_2)\rangle_2\}\\
+ % \mathtt{\ in\ }f\fill
+ \end{multline*}
+ Esto equivale en cierto modo al funtor libre $\bSet\to\bMon$.
+ \item Si tenemos un objeto de tipo $[A|E]$, donde un elemento de $A$
+ representa el resultado de un cálculo completado con éxito y uno de $E$
+ representa que ha habido un error, es común querer realizar alguna
+ operación con el resultado exitoso pero devolver los errores tal
+ cual. Esto se puede hacer con un funtor para cada tipo de error $e$.
+ \begin{align*}
+ \mathtt{type\ result}_{e,a} & = [\,a\,|\,e\,]\\
+ \mathtt{const\ rmap}_{e,a,b} & = \lambda f:a\to b.\lambda r:
+ \mathtt{result}_{e,a}.\,\mathtt{match}\ r_*\ \{
+ \lambda x.\langle fx\rangle_1;\ \lambda y.\langle y\rangle_2\}^*
+ \end{align*}
+ Cuando no nos interesan los detalles sobre el error y simplemente queremos
+ representar un valor que puede existir o no, podemos tomar $()$ como tipo de error.
+ \begin{align*}
+ \mathtt{type\ option}_a & = \mathtt{result}_{(),a};
+ & \mathtt{const\ optmap}_{a,b} & = \mathtt{rmap}_{(),a,b} \\
+ \mathtt{const\ some}_a : a\to\mathtt{option}_a & = \lambda x.\langle x\rangle_1^{**};
+ & \mathtt{const\ none}_a : \mathtt{option}_a & = \langle()\rangle_2^{**}
+ \end{align*}
+ \item Por supuesto, podemos definir el funtor identidad.
+ \begin{align*}
+ \mathtt{type\ id}_a & = a; & \mathtt{const\ idmap}_{a,b} & = \lambda f.\lambda x.(fx_*)^*
+ \end{align*}
+ Sin embargo, no podemos definir la composición de funtores dentro del
+ lenguaje ya que los funtores no son valores, por lo que debemos razonar
+ sobre ella desde fuera.
+ \item No todos los funtores que podemos definir en el lenguaje responden a
+ esta forma. Por ejemplo, consideremos la siguiente función:
+ \begin{align*}
+ \mathtt{const\ length}_a : \mathtt{list}_a \to \sNat
+ & = \lambda l.\,\mathtt{match}\ l\ \{\lambda x. 0;\ \lambda c. 1+\mathtt{length}_ac_2\}
+ \end{align*}
+ Cada $\mathtt{length}_a$ devuelve el número de elementos de una lista y es
+ pues un homomorfismo de monoides de un monoide libre a $\sNat$, por lo que
+ es un funtor entre monoides vistos como categorías. Como identificamos los
+ naturales con $\mathtt{list}_{()}$, esta función también se puede definir
+ de la siguiente forma:
+ \begin{align*}
+ \mathtt{const\ length}_a & = \mathtt{map}_{a,()}(\lambda x.())
+ \end{align*}
+ \end{enumerate}
+\end{example}
+
+Al permitir llevar funciones de una categoría a otra, los funtores favorecen la
+reusabilidad del código y evitan tener que escribir cierto código
+rutinario. Además, las subcategorías así definidas permiten definir funciones de
+forma más limpia. Por ejemplo, la función $\mathtt{last}_a$ vista anteriormente
+se puede escribir mejor como sigue:
+\begin{multline*}
+ \mathtt{const\ last}_a:\mathtt{list}_a\to\mathtt{option}_a =\\
+ \lambda l.\,\mathtt{match}\ l\ \{\lambda x.\mathtt{none}_a;\,\lambda c.\,\mathtt{match}\ c_2\ \{
+ \lambda x.\mathtt{some}_ac_1;\ \mathtt{last}_a\}\}.
+\end{multline*}
+La definición es similar, pero en vez de devolver $\bot$ en caso de error,
+devuelve $\mathtt{none}_a$, permitiendo a quien llamó a la función recuperarse
+del error o propagarlo con \texttt{optmap}. Otra observación es que
+$\mathtt{last}_a$ es una transformación natural, es decir, para todo morfismo
+$f:a\to b$,
+$\mathtt{last}_b\circ\mathtt{map}_{a,b}f=\mathtt{optmap}_{a,b}f\circ\mathtt{last}_a$.
+Intuitivamente esto tiene sentido, pues las transformaciones naturales son
+funciones que <<actúan igual>> sobre todos los tipos y las funciones con un
+parámetro de tipo cumplen esto. Lo curioso, sin embargo, es que de hecho esto
+ocurre con todas las funciones de esta forma.
+
+\begin{theorem}
+ Dadas dos expresiones de tipo $S(a)$ y $T(a)$ que dependen de una variable
+ libre $a$, si $S$ y $T$ son las funciones sobre objetos de dos endofuntores
+ $S,T:\bType\to\bType$, entonces toda familia de funciones $f_a:S(a)\to T(a)$
+ define una transformación natural $f:S\to T$.
+\end{theorem}
+
+La demostración de este teorema es complicada y se basa en el teorema de
+parametricidad de Reynolds\cite{typeabs}, que permite demostrar propiedades de
+valores a partir de sus tipos.
+
+\section{Mónadas}
+
+El uso de endofuntores permite trasladar funciones para usarlas en subcategorías
+que nos resulten más convenientes. Sin embargo, también puede ser deseable
+trabajar en categorías que no sean subcategorías de $\bType$, y en ese aspecto
+las categorías de Kleisli son muy útiles en la práctica, como se muestra en
+\cite{monad}. Esto se debe a que el uso de estas categorías permite tratar
+funciones de tipo $a\to Tb$ <<como si fueran>> de tipo $a\to b$, lo que es útil
+porque $Tb$ suele representar un resultado del tipo $b$ <<con alguna pega>>,
+como que en vez de un valor de tipo $b$ puede haber un error, o una cantidad
+variable de elementos de tipo $b$.
+
+Veamos cómo implementar categorías de Kleisli de forma eficiente. Una secuencia
+de transformaciones sobre una entrada podría tener el aspecto siguiente:
+\[
+ \mathtt{let }x_1=e_1\mathtt{ and }x_2=e_2\mathtt{ and }\dots\mathtt{ in }gx_n.
+\]
+Esta expresión equivale a
+$(\lambda x_1.(\lambda x_2.\dots((\lambda x_n.gx_n)(e_n)\dots))(e_2))(e_1)$,
+aunque es más fácil de leer y de escribir, y si cada $e_{i+1}$ solo depende de
+$x_i$, esto equivale a
+$(g\circ(\lambda x_{n-1}.e_n)\circ\dots\circ(\lambda x_1.e_2))(e_1)$, de modo
+que las secuencias de transformaciones son, en cierto modo, análogas a
+composiciones de funciones.
+
+Si ahora $(T,\eta,\mu)$ es una mónada en $\bType$ y $\hat\circ$ es la
+composición en $\bType$, una composición de la forma
+$f_n\hat\circ\dots\hat\circ f_2\hat\circ f_1$ con cada $f_i:a_{i-1}\to Ta_i$ en
+$\bType$ equivale a
+$\mu_{a_n}\circ Tf_n\circ\dots\circ\mu_{a_2}\circ Tf_2\circ f_1$. Parece
+entonces razonable definir una función parametrizada
+$\sigma_{a,b}:(a\to Tb)\to(Ta\to Tb)$ como
+$\sigma_{a,b}f\coloneqq\mu_b\circ Tf$, y entonces
+$f_n\hat\circ\dots\hat\circ f_2\hat\circ f_1$ equivale a
+$\sigma f_n\circ\dots\circ\sigma f_2\circ f_1$.
+
+Esta expresión todavía está en orden inverso de lo que seria lo intuitivo para
+definir secuencias de transformaciones, por lo que en la práctica se define una
+función $(\Rightarrow)_{a,b}:T_a\to(a\to T_b)\to T_b$ como
+$x\Rightarrow_{a,b} f\coloneqq\mu_b((Tf)(x))$, y una secuencia de
+transformaciones en $\cC_T$ se puede expresar de forma relativamente conveniente
+como
+$e_1\Rightarrow(\lambda x_1.e_2\Rightarrow(\lambda x_2.\dots(\lambda
+x_n.gx_n)))$, o si cada expresión sólo depende del resultado de la anterior,
+como $e_1\Rightarrow(\lambda x_1.e_2)\Rightarrow\dots\Rightarrow g$.
+
+A partir de $(\Rightarrow)$ se pueden recuperar $\mu$ y la actuación de $T$
+sobre morfismos. En efecto, para un tipo $a$ y un valor $x:T^2a$,
+$(x\Rightarrow 1_{Ta})=\mu_{Ta}(1_{T^2a}x)=\mu_{Ta}x$, y para un morfismo
+$f:a\to b$ y un valor $x:a$,
+$(x\Rightarrow(\eta_b\circ f))=\mu_b(T\eta_b((Tf)(x)))=(Tf)(x)$. Es por ello
+que, en lenguajes de programación funcionales como Haskell, las mónadas se
+definen como un sinónimo de tipo con un parámetro junto con definiciones para
+$\eta$ y $(\Rightarrow)$.\cite[p. 6]{haskmon}
+
+\begin{example}
+ Algunos de los ejemplos en (\ref{ex:monads}) son mónadas.
+ \begin{enumerate}
+ \item En la mónada identidad, $x\Rightarrow f$ es la evaluación $f(x)$.
+ \item El funtor $\mathtt{result}_d$ admite la mónada dada por el ejemplo
+ \ref{enu:monad-result} en dicha lista. Sean $d$ es el tipo de los errores,
+ $x:[\,b\,|\,d\,]$ y $f:b\to[\,c|\,d]$, si $x$ es un elemento de $b$,
+ $x\Rightarrow b$ equivale a $fx$ viendo $x$ como elemento de $b$, y si $x$
+ está en $d$, $x\Rightarrow f$ devuelve $x$ visto como elemento de
+ $[\,c|\,d\,]$.
+ \begin{align*}
+ \eta_a & = \lambda x.\langle x\rangle_1
+ & (\Rightarrow)_{a,b} & = \lambda x.\lambda f.\,\mathtt{match}\ x\ \{f;\ \lambda e.e\}
+ \end{align*}
+ La idea es que, si ocurre un error en un paso de la secuencia de
+ operaciones, la ejecución termina inmediatamente devolviendo el error, y
+ cuando una operación termina con éxito, el resultado pasa al siguiente paso.
+ \item El funtor \texttt{option} también admite una mónada, en tanto que es un
+ caso especial de \texttt{result}.
+ \item El funtor \texttt{list}, en tanto que se puede considerar como un funtor
+ libre, admite una mónada. Entonces $x\Rightarrow f$ aplica $f$ a cada
+ elemento de la lista $x$ y concatena las listas de resultados. Esta mónada
+ se puede usar para representar secuencias de operaciones en las que cada
+ operación no devuelve necesariamente un sólo resultado, sino que devuelve
+ una cantidad variable, generalmente pequeña, de resultados.
+ \item La mónada $\power$ se puede usar en un contexto teórico para representar
+ cómputos no deterministas. El funcionamiento de esta mónada es similar al de
+ \texttt{list} pero basándose en un conjunto en vez de en una lista.
+ \item La mónada $S$ definida en el ejemplo \ref{enu:monad-state} de
+ (\ref{ex:monads}) permite representar un estado como un elemento de $s$. La
+ expresión $x\Rightarrow f$ devuelve una función que primero evalúa $x$ sobre
+ su parámetro para obtener un par $(x',s')$ y entonces ejecuta
+ $f(x')(s')$. La idea es que esto actúa como una especie de composición de
+ funciones, de modo que el valor devuelto por una secuencia de operaciones es
+ una función compuesta $s\to(x,s)$, que recibe un estado inicial de tipo $s$
+ y devuelve un valor resultado $x$ y un estado final, también de tipo
+ $s$. Las operaciones definidas por $\eta$, no usan el estado, pero otras
+ operaciones pueden acceder al estado y devolver de vuelta un estado
+ modificado. Esto permite representar casos en los que tener un estado e ir
+ modificándolo es conveniente.
+ \end{enumerate}
+\end{example}
+
+Hemos mencionado en la introducción de este capítulo que es posible modelar la
+interactividad mediante categorías de Kleisli. Esto se suele hacer con una
+mónada especial, generalmente llamada \texttt{IO}, cuyos valores representan
+acciones, secuencias de acciones o programas enteros. Dado un tipo $a$, el tipo
+$\mathtt{IO}_a$ es el tipo de las acciones que, al ejecutarlas, realizan
+cálculos, leen datos de entrada, escriben datos de salida, etc., y finalmente
+devuelven un elemento de tipo $a$.
+
+Generalmente $\mathtt{IO}_a$ se define de forma extrínseca a partir de los
+valores que se pueden construir, que dependen de las operaciones que se quieran
+soportar. A nivel más básico, si \texttt{Char} es el tipo de los caracteres,
+podemos definir las operaciones
+\begin{align*}
+ \mathtt{putChar} & : \mathtt{Char} \to \mathtt{IO}_{()}, & \mathtt{getChar} & : \mathtt{IO}_{\mathtt{Char}},
+\end{align*}
+que imprimen un caracter en la terminal y leen un caracter del teclado,
+respectivamente, aunque también se pueden definir operaciones básicas para otros
+propósitos como dibujar en la pantalla para crear interfaces gráficas, recibir
+movimientos del ratón, manipular ficheros y directorios, conectarse a Internet,
+etc.
+
+Las operaciones en \texttt{IO} son las siguientes:
+\begin{enumerate}
+\item $\eta_a:a\to\mathtt{IO}\,a$ recibe un valor y devuelve la acción que, sin
+ hacer nada, devuelve dicho valor.
+\item Si $x:\mathtt{IO}\,a$ y $f:a\to\mathtt{IO}\,b$, $x\Rightarrow f$ devuelve
+ la acción consistente en ejecutar la acción $x$, pasar el resultado devuelto a
+ $f$ y ejecutar la acción devuelta por $f$, devolviendo su valor.
+\item Si $x:\mathtt{IO}(\mathtt{IO}\,a)$, $\mu_ax$ es la acción que ejecuta $x$
+ y, a continuación, ejecuta la acción devuelta por $x$, devolviendo su valor.
+\item Si $f:a\to b$, $\mathtt{IO}\,f:\mathtt{IO}\,a\to\mathtt{IO}\,b$ toma una
+ acción $x$ y devuelve una acción que ejecuta $x$ y devuelve el resultado de
+ aplicar $f$ al valor devuelto por la acción.
+\end{enumerate}
+
+En la práctica es común querer combinar la acción de varias mónadas. Para ello
+se usan \conc{transformadores de mónadas}, que no son otra cosa que
+transformaciones naturales en $\Mnd{\bType}$ cuyo dominio es el funtor
+identidad. Por ejemplo, en vez de definir la mónada \texttt{list} directamente,
+se define una transformación natural $\mathtt{listT}:1_{\Mnd{\bType}}\to F$ para
+cierto endofuntor $F$ en $\Mnd{\bType}$ de forma que, si $I$ es la mónada
+identidad, $FI=\mathtt{list}$.\cite{monad-trans} Es común aplicar varios de
+estos transformadores de mónadas a la vez sobre la mónada identidad o sobre la
+mónada \texttt{IO}.
+
+Finalmente, decir que las mónadas y categorías de Kleisli no sólo son útiles
+para programación funcional, sino que también lo son para razonar sobre
+programas en paradigmas convencionales y, de hecho, esta es la motivación que se
+usa en \cite{monads}, el artículo que introduce las mónadas en la teoría de la
+computación. La idea es que, por lo general, los aspectos no funcionales de los
+lenguajes de programación habituales se pueden representar mediante mónadas. Por
+ejemplo, \texttt{IO} permite comunicarse con el exterior, \texttt{state} permite
+manipular un estado global, $\power$ permite representar cómputos no
+deterministas, \texttt{result} puede usarse para modelar las excepciones,
+etc. Combinar estos efectos mediante transformadores de mónadas permite definir
+una semántica denotativa para lenguajes de programación imperativos, lo que a su
+vez permite razonar sobre programas imperativos con un alto nivel de abstracción.
+
+% Lo primero que debemos hacer es fijar
+% % un conjunto $P$ de \conc{tipos primitivos},
+% un conjunto infinito $V$ de \conc{variables de tipo}, y entonces:
+% % y un conjunto $L$ de
+% % \conc{literales},
+
+% \begin{definition}
+% Una \conc{expresión de tipo} es una expresión de alguna de las siguientes
+% formas:
+% \begin{enumerate}
+% \item Un elemento % de $P$ o
+% de $V$.
+% \item Una expresión $a\to b$, donde $a$ y $b$ son expresiones de tipo.
+% \item Una expresión $\forall a.x$, donde $a$ es una variable de tipo y $x$ es
+% una expresión de tipo.
+% \end{enumerate}
+% Un \emph{tipo} es una expresión de tipo en que toda aparición de una variable
+% de tipo $v$ está dentro de una expresión de tipo de la forma $\forall v.x$.
+% \end{definition}
+
+% \begin{definition}
+% Una \conc{expresión del lenguaje} es una expresión de alguna de las siguientes
+% formas:
+% \begin{enumerate}
+% \item Un elemento de $I$% o de $L$
+% .
+% \item El identificador \texttt{fix}.
+% \item Una expresión $ef$, donde $e$ y $f$ son expresiones del lenguaje.
+% \item Una expresión $\lambda x.e$, donde $x$ es un identificador y $e$ es una
+% expresión del lenguaje.
+% \end{enumerate}
+% Una expresión del lenguaje es \conc{cerrada} si toda aparición de un
+% identificador $x$ se encuentra dentro de una expresión de la forma
+% $\lambda x.e$.
+% \end{definition}
+
+% Las expresiones $\lambda x.e$ y $\Lambda\alpha.\tau$ establecen blindajes de la
+% variable $x$ o $\alpha$ en la expresión $e$ o $\tau$. Entonces los conceptos de
+% variable libre y cerrada se definen de la forma habitual en lógica, y esto a su
+% vez nos permite definir sustituciones de variables $e[f/x]$ y
+% $\tau[\sigma/\alpha]$ de la manera habitual.
+
+% Para una expresión $e$ y un tipo $T$, decimos que $e$ es de tipo $T$, $e:T$, si
+% lo es de acuerdo a las siguientes reglas de inferencia:
+
+% \begin{description}
+% \item[Tautología] $\Gamma,x:T\vdash x:T$.
+% \item[Instanciación]
+% $\logicrule{\Gamma\vdash
+% e:\forall\alpha_1.\dots\forall\alpha_n.\tau}{\Gamma\vdash
+% e:\forall\beta_1.\dots\forall\beta_m.\nu}$, si expresiones de tipo
+% $\sigma_1,\dots,\sigma_n$ tales que $\nu=\tau[\sigma_i/\alpha_i]_i$ y
+% $\forall\beta_1.\dots\forall\beta_m.\nu$ es un tipo.
+% \item[Generalización] $\logicrule{\Gamma\vdash e:T}{\Gamma\vdash\forall\alpha.T}$.
+% \item[Combinación]
+% $\logicrule{\Gamma\vdash e:T\to T',\quad\Gamma\vdash e':T}{\Gamma\vdash
+% ee':T'}$.
+% \item[Abstracción] $\logicrule{\Gamma,x:T\vdash e:T'}{\Gamma\vdash\lambda x.e:T\to T'}$.
+% \item[Recursión] $\mathtt{fix}:\forall\alpha.((\alpha\to\alpha)\to\alpha)$.
+% \end{description}
+
+% % La idea es que se parte de un conjunto de axiomas formado por un axioma de la
+% % forma $x:T$ para cada $x\in L$.
+% La sintaxis $\lambda x.e$ es similar a la sintaxis $x\mapsto e$ para funciones
+% matemáticas, y la yuxtaposición de expresiones equivale a la aplicación de una
+% función. La instanciación y generalización tienen que ver con el polimorfismo,
+% para poder definir funciones genéricas, y \texttt{fix} hace al lenguaje
+% Turing-completo al permitir la recursividad, como veremos posteriormente.
+
+% También definimos una noción de expresiones equivalentes, que <<representan el
+% mismo valor>>. % Para tipos primitivos, esta definición viene dada por un conjunto
+% % $E$ de pares $(e,f)$ de expresiones del mismo tipo, y para expresiones generales
+% % tenemos la definición siguiente.
+
+% \begin{definition}
+% Dos expresiones del lenguaje $e$ y $f$ del mismo tipo son \emph{equivalentes}
+% si están en la misma clase de equivalencia bajo la menor relación de
+% equivalencia $~$ tal que:
+% \begin{enumerate}
+% \item $\lambda x.e~\lambda y.e[y/x]$, si $y$ no es libre en $e$.
+% \item $(\lambda x.e)f~e[f/x]$.
+% \item $\lambda x.(ex)~e$, si $x$ no es libre en $e$.
+% \item Si se puede pasar de $e$ a $f$ sustituyendo una subexpresión de $e$ por
+% otra equivalente, entonces $e$ y $f$ son equivalentes.
+% % \item Si $(e,f)\in E$ entonces $e~f$.
+% \item $\mathtt{fix}f~f(\mathtt{fix}f)$.
+% \end{enumerate}
+% \end{definition}
+
+% Aunque no vamos a demostrarlo aquí, esta definición de equivalencia asegura que,
+% si $f,g:a\to b$, $f~g$ si y sólo si, para todo $x:b$, $fx~gx$.
+
+% \begin{definition}
+% La categoría $\bType$ tiene como objetos los tipos del sistema definido y como
+% morfismos $a\to b$ el conjunto cociente de las expresiones de tipo $a\to b$
+% bajo la equivalencia de expresiones. Si $\overline e$ es la clase de
+% equivalencia de una expresión $e$, la composición de dos morfismos
+% $\overline f:a\to b$ y $\overline g:b\to c$ viene dada por
+% $\overline g\circ\overline f\coloneqq\lambda x.g(fx)$, y la identidad en un
+% objeto $a$ viene dada por $1_a\coloneqq\overline{\lambda x.x}$.
+% \end{definition}
+
+% Con esto, si a cada tipo le asociamos el conjunto cociente de las expresiones de
+% ese tipo bajo equivalencia de expresiones, es posible interpretar los morfismos
+% como funciones entre dichos conjuntos. Esto define un funtor olvidadizo
+% $\bType\to\bSet$ que, sin embargo no admite un funtor libre, pues $\Ob{\bType}$
+% es numerable y $\Ob{\bSet}$ no lo es.
+
+% Una expresión es normalizable si es equivalente a una que no contiene
+% subexpresiones de la forma $(\lambda x.e)f$. La idea es que calcular el valor de
+% una expresión consiste en eliminar las subexpresiones de esta forma de izquierda
+% a derecha hasta que no quede ninguna, y las expresiones no normalizables se
+% corresponden con cálculos que no terminan.
+
+% \begin{example} Pese a que $\Ob{\bType}$ no es numerable, podemos definir
+% bastantes construcciones, como vemos a continuación.
+% \begin{enumerate}
+% \item Pongamos que queremos definir una función $f=\lambda x.e:a\to b$
+% recursiva, cuya definición contenga la propia $f$. Para ello, como
+% $\mathtt{fix}:((a\to b)\to(a\to b))\to(a\to b)$, basta definir la función
+% como $F\coloneqq\mathtt{fix}(\lambda f.\lambda x.e)$ y por las
+% equivalencias, $F~\lambda x.e[F/f]$.
+% \item %TODO Función identidad
+% \item %TODO Unidad
+% \item %TODO Vacío
+% \item %TODO Producto
+% \item %TODO Coproducto
+% \item %TODO Condicionales
+% \item El tipo $\forall\alpha.(\alpha\to\alpha)\to\alpha\to\alpha$ define los
+% números naturales. En efecto, las expresiones normalizables de este tipo son
+% esencialmente las de la forma $\lambda f.\lambda x.e$, donde $e$ solo
+% depende de $f$ y de $x$, pero $e$ no puede generar un elemento arbitrario
+% (normalizable) de $\alpha$ porque $\alpha$ es un tipo arbitrario, de modo
+% que debe aplicar $f$ un número de veces a $x$. Entonces la suma se define
+% como $(n+m)f\coloneqq nf\circ mf$ y el producto como $nm\coloneqq n\circ m$,
+% y otras operaciones se definen de manera similar.
+% \item %TODO Listas
+% \end{enumerate}
+% \end{example}
+
+% TODO Explicar vagamente la ausencia de coproductos
+% TODO Funtores sobre listas
+% TODO Transformaciones naturales
+% TODO Las mónadas típicas
+
+%%% Local Variables:
+%%% mode: latex
+%%% TeX-master: "main"
+%%% End:
diff --git a/main.tex b/main.tex
index f74a7ca..877fcd6 100644
--- a/main.tex
+++ b/main.tex
@@ -97,6 +97,7 @@ decorate, decoration={
\dCat{\bCAT}{CAT}
\dCat{\bCls}{Cls}
\dCat{\bTopVec}{TopVec}
+\dCat{\bType}{Type}
\newcommand{\cA}{\mathcal{A}}
\newcommand{\cB}{\mathcal{B}}
\newcommand{\cC}{\mathcal{C}}
@@ -136,6 +137,7 @@ decorate, decoration={
\xrightarrow[\;{#2}\hfil]{\;{#1}\hfil}}}}}
\newcommand{\nattwos}[2]{\nattwog{\downarrow\,{#1}}{\downarrow\,{#2}}{}{}}
\newcommand{\nattwoi}[2]{\nattwog{\cong\,{#1}}{\cong\,{#2}}{}{}}
+\newcommand{\logicrule}[2]{\frac{\displaystyle{#1}}{\displaystyle{#2}}}
\begin{document}
\begin{titlepage}
@@ -196,7 +198,7 @@ decorate, decoration={
\input{ch6_monads}
\chapter{Categorías en programación}
-% TODO
+\input{ch7_programming}
\appendix
%\chapter{Bibliografía}
diff --git a/ref.bib b/ref.bib
index dc27913..54ad3c1 100644
--- a/ref.bib
+++ b/ref.bib
@@ -26,4 +26,78 @@
author = {Emily Riehl},
title = {Category Theory in Context},
langid = {english}
+}
+@thesis{types,
+ author = {Martins Damas, Luis Manuel},
+ title = {Type Assignment in Programming Languages},
+ year = {1984},
+ publisher = {University of Edinburgh},
+ langid = {english}
+}
+@paper{hindley-milner,
+ author = {Damas, Luis and Milner, Robin},
+ title = {Principal type-schemes for functional programs},
+ year = {1982},
+ publisher = {Association of Computer Machinery},
+ journaltitle = {Proceedings of the 9th ACM SIGPLAN-SIGACT
+ symposium on Principles of programming languages},
+ doi = {10.1145/582153.582176},
+ url = {https://web.cs.wpi.edu/~cs4536/c12/milner-damas_principal_types.pdf},
+ langid = {english}
+}
+@techreport{coc,
+ title = {{The calculus of constructions}},
+ author = {Coquand, Thierry and Huet, G{\'e}rard},
+ url = {https://inria.hal.science/inria-00076024},
+ number = {RR-0530},
+ institution = {{INRIA}},
+ year = {1986},
+ month = May,
+ url = {https://inria.hal.science/inria-00076024/file/RR-0530.pdf},
+ doi = {10.1016/0890-5401(88)90005-3}
+}
+@InProceedings{polynoset,
+ author="Reynolds, John C.",
+ editor="Kahn, Gilles and MacQueen, David B. and Plotkin, Gordon",
+ title="Polymorphism is not set-theoretic",
+ booktitle="Semantics of Data Types",
+ year="1984",
+ publisher="Springer Berlin Heidelberg",
+ address="Berlin, Heidelberg",
+ pages="145--156",
+ isbn="978-3-540-38891-3",
+ doi={10.1007/3-540-13346-1_7},
+ url={https://link.springer.com/chapter/10.1007/3-540-13346-1_7},
+ langid={english}
+}
+@paper{typeabs,
+ author={Reynolds, John C.},
+ title={Types, abstraction and parametric polymorphism},
+ publisher={Elvesier Science Publishers B.V.},
+ year={1983},
+ journaltitle={Information Processing},
+ volume={83},
+ url={https://www.cs.cmu.edu/afs/cs/user/jcr/ftp/typesabpara.pdf},
+ langid={english}
+}
+@paper{monads,
+ author={Moggi, Eugenio},
+ title={Notions of computation and monads},
+ url={https://person.dibris.unige.it/moggi-eugenio/ftp/ic91.pdf},
+ langid={english}
+}
+@online{haskmon,
+ author={Newbern, Jeff},
+ title={All About Monads: A comprehensive guide to the theory and
+ practice of monadic programming in Haskell, v.
+ 1.1.0},
+ url={https://www.cs.rit.edu/~swm/cs561/All_About_Monads.pdf},
+ langid={english}
+}
+@online{monad-trans,
+ author={Shan, Chung-chieh},
+ title={Monad transformers},
+ url={http://conway.rutgers.edu/~ccshan/wiki/blog/posts/Monad_transformers/},
+ urldate={2023-06-20},
+ langid={english}
} \ No newline at end of file