mirror of
https://github.com/dhil/phd-dissertation
synced 2026-03-13 11:08:25 +00:00
Compare commits
2 Commits
604e8047f1
...
e2af947cbd
| Author | SHA1 | Date | |
|---|---|---|---|
| e2af947cbd | |||
| 8700c0f439 |
@@ -545,15 +545,13 @@ fun init(fsys, main) {
|
|||||||
### Stream redirection
|
### Stream redirection
|
||||||
###
|
###
|
||||||
sig >> : (Comp(a, { Create: (String) -> FileDescr
|
sig >> : (Comp(a, { Create: (String) -> FileDescr
|
||||||
, Open: (String) {}-> Option (FileDescr)
|
|
||||||
, Write:(FileDescr, String) -> () |e}), String)
|
, Write:(FileDescr, String) -> () |e}), String)
|
||||||
{ Create:(String) -> FileDescr
|
{ Create:(String) -> FileDescr
|
||||||
, Open:(String) -> Option (FileDescr)
|
, Write:(FileDescr, String) -> () |e}~> a
|
||||||
, Write:(FileDescr, String) -> () |e}~> ()
|
|
||||||
op f >> fname {
|
op f >> fname {
|
||||||
var fd = create(fname);
|
var fd = create(fname);
|
||||||
handle(f()) {
|
handle(f()) {
|
||||||
case Return(_) -> ()
|
case Return(x) -> x
|
||||||
case Write(_, cs, resume) ->
|
case Write(_, cs, resume) ->
|
||||||
resume(write(fd, cs))
|
resume(write(fd, cs))
|
||||||
}
|
}
|
||||||
|
|||||||
16
macros.tex
16
macros.tex
@@ -8,7 +8,9 @@
|
|||||||
%%
|
%%
|
||||||
%% Some useful maths abbreviations
|
%% Some useful maths abbreviations
|
||||||
%%
|
%%
|
||||||
|
\newcommand{\C}{\ensuremath{\mathbb{C}}}
|
||||||
\newcommand{\N}{\ensuremath{\mathbb{N}}}
|
\newcommand{\N}{\ensuremath{\mathbb{N}}}
|
||||||
|
\newcommand{\Z}{\ensuremath{\mathbb{Z}}}
|
||||||
\newcommand{\B}{\ensuremath{\mathbb{B}}}
|
\newcommand{\B}{\ensuremath{\mathbb{B}}}
|
||||||
\newcommand{\BB}[1]{\ensuremath{\mathbf{#1}}}
|
\newcommand{\BB}[1]{\ensuremath{\mathbf{#1}}}
|
||||||
|
|
||||||
@@ -394,6 +396,20 @@
|
|||||||
\newcommand{\Proc}{\dec{Proc}}
|
\newcommand{\Proc}{\dec{Proc}}
|
||||||
\newcommand{\schedule}{\dec{schedule}}
|
\newcommand{\schedule}{\dec{schedule}}
|
||||||
\newcommand{\fsname}{BSFS}
|
\newcommand{\fsname}{BSFS}
|
||||||
|
\newcommand{\FileSystem}{\dec{FileSystem}}
|
||||||
|
\newcommand{\Directory}{\dec{Directory}}
|
||||||
|
\newcommand{\DataRegion}{\dec{DataRegion}}
|
||||||
|
\newcommand{\INode}{\dec{INode}}
|
||||||
|
\newcommand{\IList}{\dec{IList}}
|
||||||
|
\newcommand{\fileRW}{\dec{fileRW}}
|
||||||
|
\newcommand{\fileAlloc}{\dec{fileAlloc}}
|
||||||
|
\newcommand{\URead}{\dec{Read}}
|
||||||
|
\newcommand{\UWrite}{\dec{Write}}
|
||||||
|
\newcommand{\UCreate}{\dec{Create}}
|
||||||
|
\newcommand{\fread}{\dec{fread}}
|
||||||
|
\newcommand{\fcreate}{\dec{fcreate}}
|
||||||
|
\newcommand{\Ucreate}{\dec{create}}
|
||||||
|
\newcommand{\redirect}{\texttt{>}}
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% Some control operators
|
%% Some control operators
|
||||||
|
|||||||
10
thesis.bib
10
thesis.bib
@@ -1,5 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
# Daniel's master's thesis (initial implementation of handlers in Links)
|
# Daniel's master's thesis (initial implementation of handlers in Links)
|
||||||
@MastersThesis{Hillerstrom15,
|
@MastersThesis{Hillerstrom15,
|
||||||
author = {Daniel Hillerström},
|
author = {Daniel Hillerström},
|
||||||
@@ -1554,3 +1552,11 @@
|
|||||||
publisher = {{ACM}},
|
publisher = {{ACM}},
|
||||||
year = {1993}
|
year = {1993}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Purely functional data structures
|
||||||
|
@book{Okasaki99,
|
||||||
|
author = {Chris Okasaki},
|
||||||
|
title = {Purely functional data structures},
|
||||||
|
publisher = {Cambridge University Press},
|
||||||
|
year = {1999}
|
||||||
|
}
|
||||||
288
thesis.tex
288
thesis.tex
@@ -113,6 +113,7 @@
|
|||||||
% \title{Programming Computable Effectful Functions}
|
% \title{Programming Computable Effectful Functions}
|
||||||
% \title{Handling Effectful Computations}
|
% \title{Handling Effectful Computations}
|
||||||
\title{Foundations for Programming and Implementing Effect Handlers}
|
\title{Foundations for Programming and Implementing Effect Handlers}
|
||||||
|
%\title{Foundations for Programming with Control via Effect Handlers}
|
||||||
\author{Daniel Hillerström}
|
\author{Daniel Hillerström}
|
||||||
|
|
||||||
%% If the year of submission is not the current year, uncomment this line and
|
%% If the year of submission is not the current year, uncomment this line and
|
||||||
@@ -3321,66 +3322,201 @@ evaluation of the stateful fragment of $m$ to continue.
|
|||||||
\caption{\UNIX{} directory, i-list, and data region mappings.}\label{fig:unix-mappings}
|
\caption{\UNIX{} directory, i-list, and data region mappings.}\label{fig:unix-mappings}
|
||||||
\end{figure}
|
\end{figure}
|
||||||
%
|
%
|
||||||
A file system provide an abstraction over primary storage in a
|
A file system provide an abstraction over storage media in a computer
|
||||||
computer system. This abstraction facilities allocation, deletion,
|
system by organising the storage space into a collection of files.
|
||||||
storage, and access of files.
|
This abstraction facilities typical file operations: allocation,
|
||||||
|
deletion, reading, and writing.
|
||||||
%
|
%
|
||||||
A typical \UNIX{} file system is hierarchical and
|
\UNIX{} dogmatises the notion of file to the point where
|
||||||
distinguishes between ordinary files, directories, and special
|
\emph{everything is a file}. A typical \UNIX{}-style file system
|
||||||
files~\cite{RitchieT74}. To simplify matters, our file system will be
|
differentiates between ordinary files, directory files, and special
|
||||||
entirely flat and only contain ordinary files. Although, the system
|
files~\cite{RitchieT74}. An ordinary file is a sequence of
|
||||||
can readily be extended to be hierarchical, it comes at the expense of
|
characters. A directory file is a container for all kinds of files. A
|
||||||
|
special file is an interface for interacting with an i/o device.
|
||||||
|
|
||||||
|
We will implement a \emph{basic sequential file system}, which we dub
|
||||||
|
\fsname{}.
|
||||||
|
%
|
||||||
|
It will be basic in the sense that it models the bare minimum to pass
|
||||||
|
as a file system, that is we will implement support for the four basic
|
||||||
|
operations: file allocation, file deletion, file reading, and file
|
||||||
|
writing.
|
||||||
|
%
|
||||||
|
The system will support sequential read and write operations. This
|
||||||
|
means every file is read in order from its first character to its
|
||||||
|
character, and every file is written to by appending the new content.
|
||||||
|
%
|
||||||
|
\fsname{} will only contain ordinary files, and as a result
|
||||||
|
the file hierarchy will be entirely flat. Although, the system can
|
||||||
|
readily be extended to be hierarchical, it comes at the expense of
|
||||||
extra complexity, that blurs rather than illuminates the model.
|
extra complexity, that blurs rather than illuminates the model.
|
||||||
|
|
||||||
|
\paragraph{Directory, i-list, and data region}
|
||||||
%
|
%
|
||||||
An ordinary file is a sequence of characters, or a simply a string in
|
The core of \fsname{} comprises three structures to manage interaction
|
||||||
our setting.
|
with files.
|
||||||
|
\begin{enumerate}
|
||||||
|
\item The \emph{directory} is a collection of human-readable names for
|
||||||
|
files. In general, a file may have multiple names. Each name is
|
||||||
|
stored along with a pointer into the i-list.
|
||||||
|
\item The \emph{i-list} is a collection of i-nodes. Each i-node
|
||||||
|
contains the meta-data for a file along with a pointer into the data
|
||||||
|
region.
|
||||||
|
\item The \emph{data region} contains the actual file contents.
|
||||||
|
\end{enumerate}
|
||||||
%
|
%
|
||||||
|
Figure~\ref{fig:unix-mappings} depicts an example with the three
|
||||||
|
structures and a mapping between them.
|
||||||
Figure~\ref{fig:unix-mappings} depicts some example mappings from the
|
%
|
||||||
directory into the i-list, and from there into the data region.
|
The only file meta-data tracked by \fsname{} is the number of names
|
||||||
|
for a file.
|
||||||
Mathematically, the mapping from i-list to the data region is a
|
%
|
||||||
partial bijection.
|
The three structures and their mappings can be implemented using
|
||||||
|
association lists. Although, a better practical choice may be a
|
||||||
|
functional map or functional array~\cite{Okasaki99}, association lists
|
||||||
|
have the advantage of having a simple, straightforward implementation.
|
||||||
%
|
%
|
||||||
\[
|
\[
|
||||||
\ba{@{~}l@{\qquad}c@{~}l}
|
\ba{@{~}l@{\qquad}c@{~}l}
|
||||||
\dec{Directory} \defas \List~\Record{\String;\Int} &&%
|
\Directory \defas \List~\Record{\String;\Int} &&%
|
||||||
\dec{DataRegion} \defas \List~\Record{\Int;\UFile} \smallskip\\
|
\DataRegion \defas \List~\Record{\Int;\UFile} \smallskip\\
|
||||||
\dec{INode} \defas \Record{loc:\Int;lno:\Int} &&%
|
\INode \defas \Record{lno:\Int;loc:\Int} &&%
|
||||||
\dec{IList} \defas \List~\Record{\Int;\dec{INode}}
|
\IList \defas \List~\Record{\Int;\INode}
|
||||||
\ea
|
\ea
|
||||||
\]
|
\]
|
||||||
%
|
%
|
||||||
|
Mathematically, we may think the type $\dec{Directory}$ as denoting a
|
||||||
|
partial function $\C^\ast \pto \Z$, where $\C$ is a suitable
|
||||||
|
alphabet. The function produces an index into the i-list.
|
||||||
|
%
|
||||||
|
Similarly, the type $\dec{IList}$ denotes a partial function
|
||||||
|
$\Z \pto \Z \times \Z$, where the codomain is the denotation of
|
||||||
|
$\dec{INode}$. The first component of the pair is the number of names
|
||||||
|
linked to the i-node, and as such $\Z$ is really an overapproximation
|
||||||
|
as an i-node cannot have a negative number of names. The second
|
||||||
|
component is an index into the data region.
|
||||||
|
%
|
||||||
|
The denotation of the type $\dec{DataRegion}$ is another partial
|
||||||
|
function $\Z \pto \C^\ast$.
|
||||||
|
|
||||||
|
We define the type of the file system to be a record of the three
|
||||||
|
association lists along with two counters for the next available index
|
||||||
|
into the data region and i-list, respectively.
|
||||||
|
%
|
||||||
\[
|
\[
|
||||||
\dec{FileSystem} \defas \Record{
|
\FileSystem \defas \Record{
|
||||||
\ba[t]{@{}l}
|
\ba[t]{@{}l}
|
||||||
dir:\dec{Directory},ilist:\dec{IList},dreg:\dec{DataRegion},\\
|
dir:\Directory;ilist:\IList;dreg:\DataRegion;\\
|
||||||
dnext:\Int,inext:\Int}
|
dnext:\Int;inext:\Int}
|
||||||
\ea
|
\ea
|
||||||
\]
|
\]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
\begin{figure}[t]
|
|
||||||
\centering
|
|
||||||
\begin{tikzpicture}[node distance=4cm,auto,>=stealth']
|
|
||||||
\node[] (server) {\bfseries Bob (server)};
|
|
||||||
\node[left = of server] (client) {\bfseries Alice (client)};
|
|
||||||
\node[below of=server, node distance=5cm] (server_ground) {};
|
|
||||||
\node[below of=client, node distance=5cm] (client_ground) {};
|
|
||||||
%
|
%
|
||||||
\draw (client) -- (client_ground);
|
We can then give an implementation of the initial state of the file
|
||||||
\draw (server) -- (server_ground);
|
system.
|
||||||
\draw[->,thick] ($(client)!0.25!(client_ground)$) -- node[rotate=-6,above,scale=0.7,midway]{SYN 42} ($(server)!0.40!(server_ground)$);
|
%
|
||||||
\draw[<-,thick] ($(client)!0.56!(client_ground)$) -- node[rotate=6,above,scale=0.7,midway]{SYN 84;ACK 43} ($(server)!0.41!(server_ground)$);
|
\[
|
||||||
\draw[->,thick] ($(client)!0.57!(client_ground)$) -- node[rotate=-6,above,scale=0.7,midway]{ACK 85} ($(server)!0.72!(server_ground)$);
|
\dec{fs}_0 \defas \Record{
|
||||||
\end{tikzpicture}
|
\ba[t]{@{}l}
|
||||||
\caption{Sequence diagram for the TCP handshake example.}\label{fig:tcp-handshake}
|
dir=[\Record{\strlit{stdout};0}];ilist=[\Record{0;\Record{lno=1;loc=0}}];dreg=[\Record{0;\strlit{}}];\\
|
||||||
\end{figure}
|
dnext=1;inext=1}
|
||||||
|
\ea
|
||||||
|
\]
|
||||||
|
%
|
||||||
|
Initially the file system contains a single, empty file with the name
|
||||||
|
$\texttt{stdout}$.
|
||||||
|
|
||||||
\paragraph{TCP threeway handshake}
|
\paragraph{File reading and writing}
|
||||||
|
%
|
||||||
|
\[
|
||||||
|
\bl
|
||||||
|
\fileRW : (\UnitType \to \alpha \eff \{\URead : \Int \opto \Option\,\String;\UWrite : \Record{\Int;\String} \opto \UnitType\}) \to \alpha \eff \{\State~\FileSystem\}\\
|
||||||
|
\fileRW~m \defas
|
||||||
|
\ba[t]{@{}l}
|
||||||
|
\Handle\;m\,\Unit\;\With\\
|
||||||
|
~\ba{@{~}l@{~}c@{~}l}
|
||||||
|
\Return\;res &\mapsto& res\\
|
||||||
|
\OpCase{\URead}{ino}{resume} &\mapsto&
|
||||||
|
\bl
|
||||||
|
\Let\;cs\revto \faild\,\Record{\None;\lambda\Unit.\\
|
||||||
|
\quad\Some\,(\fread\,\Record{ino;\Uget\,\Unit})}\\
|
||||||
|
\In\;resume\,cs
|
||||||
|
\el\\
|
||||||
|
\OpCase{\UWrite}{\Record{ino;cs}}{resume} &\mapsto&
|
||||||
|
\ba[t]{@{}l}
|
||||||
|
\faild~\Record{\Unit; \lambda \Unit.\\
|
||||||
|
\quad\bl
|
||||||
|
\Let\;fs \revto \fwrite\,\Record{ino;cs;\Uget~\Unit}\\
|
||||||
|
\In\;\Uput~fs};\,resume\,\Unit
|
||||||
|
\el
|
||||||
|
\ea
|
||||||
|
\ea
|
||||||
|
\ea
|
||||||
|
\el
|
||||||
|
\]
|
||||||
|
|
||||||
|
\paragraph{File allocation}
|
||||||
|
%
|
||||||
|
\[
|
||||||
|
\bl
|
||||||
|
\fileAlloc : (\UnitType \to \alpha \eff \{\UCreate : \String \opto \Int\}) \to \alpha \eff \{\State~\FileSystem\}\\
|
||||||
|
\fileAlloc~m \defas
|
||||||
|
\ba[t]{@{}l}
|
||||||
|
\Handle\;m\,\Unit\;\With\\
|
||||||
|
~\ba{@{~}l@{~}c@{~}l}
|
||||||
|
\Return\;res &\mapsto& res\\
|
||||||
|
\OpCase{\UCreate}{fname}{resume} &\mapsto&
|
||||||
|
\bl
|
||||||
|
\Let\;ino \revto \faild\,\Record{-1; \lambda\Unit.\\
|
||||||
|
\quad\bl
|
||||||
|
\Let\;\Record{ino;fs} = \fcreate\,\Record{\,fname;\Uget\,\Unit}\\
|
||||||
|
\In\;\Uput~fs;\,ino}
|
||||||
|
\el\\
|
||||||
|
\In\; resume\,ino
|
||||||
|
\el
|
||||||
|
\ea
|
||||||
|
\ea
|
||||||
|
\el
|
||||||
|
\]
|
||||||
|
|
||||||
|
\paragraph{Stream redirection}
|
||||||
|
%
|
||||||
|
\[
|
||||||
|
\bl
|
||||||
|
\redirect : \Record{\UnitType \to \alpha \eff \{\Write : \Record{\Int;\String} \opto \UnitType\}; \String} \to \alpha \eff \{\UCreate : \String \opto \Int;\Write : \Record{\Int;\String}\}\\
|
||||||
|
m~\redirect~fname \defas
|
||||||
|
\ba[t]{@{}l}
|
||||||
|
\Let\;ino \revto \Ucreate~fname\;\In\\
|
||||||
|
\Handle\;m\,\Unit\;\With\\
|
||||||
|
~\ba{@{~}l@{~}c@{~}l}
|
||||||
|
\Return\;res &\mapsto& res\\
|
||||||
|
\OpCase{\Write}{\Record{\_;cs}}{resume} &\mapsto& resume\,(\Do\;\Write\,\Record{ino;cs})
|
||||||
|
\ea
|
||||||
|
\ea
|
||||||
|
\el
|
||||||
|
\]
|
||||||
|
%
|
||||||
|
|
||||||
|
\paragraph{File linking and unlinking}
|
||||||
|
\dhil{Maybe axe\dots}
|
||||||
|
|
||||||
|
% \begin{figure}[t]
|
||||||
|
% \centering
|
||||||
|
% \begin{tikzpicture}[node distance=4cm,auto,>=stealth']
|
||||||
|
% \node[] (server) {\bfseries Bob (server)};
|
||||||
|
% \node[left = of server] (client) {\bfseries Alice (client)};
|
||||||
|
% \node[below of=server, node distance=5cm] (server_ground) {};
|
||||||
|
% \node[below of=client, node distance=5cm] (client_ground) {};
|
||||||
|
% %
|
||||||
|
% \draw (client) -- (client_ground);
|
||||||
|
% \draw (server) -- (server_ground);
|
||||||
|
% \draw[->,thick] ($(client)!0.25!(client_ground)$) -- node[rotate=-6,above,scale=0.7,midway]{SYN 42} ($(server)!0.40!(server_ground)$);
|
||||||
|
% \draw[<-,thick] ($(client)!0.56!(client_ground)$) -- node[rotate=6,above,scale=0.7,midway]{SYN 84;ACK 43} ($(server)!0.41!(server_ground)$);
|
||||||
|
% \draw[->,thick] ($(client)!0.57!(client_ground)$) -- node[rotate=-6,above,scale=0.7,midway]{ACK 85} ($(server)!0.72!(server_ground)$);
|
||||||
|
% \end{tikzpicture}
|
||||||
|
% \caption{Sequence diagram for the TCP handshake example.}\label{fig:tcp-handshake}
|
||||||
|
% \end{figure}
|
||||||
|
|
||||||
|
% \paragraph{TCP threeway handshake}
|
||||||
|
|
||||||
% The existing literature already contain an extensive amount of
|
% The existing literature already contain an extensive amount of
|
||||||
% introductory examples of programming with (deep) effect
|
% introductory examples of programming with (deep) effect
|
||||||
@@ -3661,6 +3797,68 @@ partial bijection.
|
|||||||
\subsection{Dynamic semantics}
|
\subsection{Dynamic semantics}
|
||||||
|
|
||||||
\subsection{\UNIX{}-style pipes}
|
\subsection{\UNIX{}-style pipes}
|
||||||
|
\label{sec:pipes}
|
||||||
|
|
||||||
|
With shallow handlers we define a demand-driven Unix pipeline operator
|
||||||
|
as follows.
|
||||||
|
%
|
||||||
|
\[
|
||||||
|
\ba{@{}c@{}}
|
||||||
|
\ba{@{}r@{~}c@{~}l@{}l@{~}c@{~}l@{}}
|
||||||
|
\Pipe &:& \Record{\UnitType \to \alpha ! \{ \Yield : \beta \opto \UnitType; \varepsilon \}, &\UnitType \to \alpha!\{ \Await : \UnitType \opto \beta; \varepsilon \}} &\to& \alpha!\{\varepsilon\} \\
|
||||||
|
\Copipe &:& \Record{\beta \to \alpha!\{ \Await : \UnitType \opto \beta; \varepsilon\}, &\UnitType \to \alpha!\{ \Yield : \beta \opto \UnitType; \varepsilon\}} &\to& \alpha!\{\varepsilon\} \\
|
||||||
|
\ea \medskip \\
|
||||||
|
\ba{@{}l@{\hspace{-0em}}@{\hspace{-2mm}}l@{}}
|
||||||
|
\ba[t]{@{}l@{}}
|
||||||
|
\Pipe\, \Record{p; c} \defas \\
|
||||||
|
\quad
|
||||||
|
\bl
|
||||||
|
\ShallowHandle\; c\,\Unit \;\With\; \\
|
||||||
|
\quad
|
||||||
|
\ba[m]{@{}l@{~}c@{~}l@{}}
|
||||||
|
\Return~x &\mapsto& x \\
|
||||||
|
\Await~\Unit~resume &\mapsto& \Copipe\,\Record{resume; p} \\
|
||||||
|
\ea \\
|
||||||
|
\el
|
||||||
|
\ea &
|
||||||
|
\ba[t]{@{}l@{}}
|
||||||
|
\Copipe\, \Record{c; p} \defas \\
|
||||||
|
\quad
|
||||||
|
\bl
|
||||||
|
\ShallowHandle\; p\,\Unit \;\With\; \\
|
||||||
|
\quad
|
||||||
|
\ba[m]{@{}l@{~}c@{~}l@{}}
|
||||||
|
\Return~x &\mapsto& x \\
|
||||||
|
\Yield~s~resume &\mapsto& \Pipe\,\Record{resume; \lambda \Unit. c\, s} \\
|
||||||
|
\ea \\
|
||||||
|
\el \\
|
||||||
|
\ea \\
|
||||||
|
\ea \\
|
||||||
|
\ea
|
||||||
|
\]
|
||||||
|
%
|
||||||
|
A pipe takes two suspended computations, a producer $p$ and a consumer
|
||||||
|
$c$.
|
||||||
|
%
|
||||||
|
Each of the computations returns a value of type $\alpha$.
|
||||||
|
%
|
||||||
|
The producer can perform the $\Yield$ operation, which yields a value
|
||||||
|
of type $\beta$ and the consumer can perform the $\Await$ operation,
|
||||||
|
which correspondingly awaits a value of type $\beta$.
|
||||||
|
%
|
||||||
|
The shallow handler $\Pipe$ runs the consumer first. If the consumer
|
||||||
|
terminates with a value, then the $\Return$ clause is executed and
|
||||||
|
returns that value as is. If the consumer performs the $\Await$
|
||||||
|
operation, then the $\Copipe$ handler is invoked with the resumption
|
||||||
|
of the consumer ($resume$) and the producer ($p$) as arguments.
|
||||||
|
|
||||||
|
The $\Copipe$ function runs the producer to get a value to feed to the
|
||||||
|
waiting consumer.
|
||||||
|
% The arguments are swapped and the consumer component
|
||||||
|
% now expects a value.
|
||||||
|
If the producer performs the $\Yield$ operation, then $\Pipe$ is
|
||||||
|
invoked with the resumption of the producer along with a thunk that
|
||||||
|
applies the consumer's resumption to the yielded value.
|
||||||
|
|
||||||
\begin{example}[Inversion of control]
|
\begin{example}[Inversion of control]
|
||||||
foo
|
foo
|
||||||
@@ -3678,7 +3876,7 @@ can simulate deep handlers up to congruence, and that deep handlers
|
|||||||
can simulate shallow handlers up to administrative reductions. The
|
can simulate shallow handlers up to administrative reductions. The
|
||||||
latter construction generalises the example of pipes implemented using
|
latter construction generalises the example of pipes implemented using
|
||||||
deep handlers that we gave in
|
deep handlers that we gave in
|
||||||
Section~\ref{sec:shallow-handlers-tutorial}.
|
Section~\ref{sec:pipes}.
|
||||||
%
|
%
|
||||||
|
|
||||||
\subsection{Deep as shallow}
|
\subsection{Deep as shallow}
|
||||||
|
|||||||
Reference in New Issue
Block a user