1
0
mirror of https://github.com/dhil/phd-dissertation synced 2026-03-13 02:58:26 +00:00

Effect sugar

This commit is contained in:
2021-05-25 12:59:01 +01:00
parent b47c60fe57
commit 650de85238
2 changed files with 206 additions and 101 deletions

View File

@@ -383,16 +383,18 @@
\newcommand{\conf}{\mathcal{C}} \newcommand{\conf}{\mathcal{C}}
% effect sugar % effect sugar
\newcommand{\xcomp}[1]{\sembr{#1}} \newcommand{\inward}[1]{\mathcal{I}\sembr{#1}}
\newcommand{\xval}[1]{\sembr{#1}} \newcommand{\outward}[1]{\mathcal{O}\sembr{#1}}
\newcommand{\xpre}[1]{\sembr{#1}} \newcommand{\xcomp}[1]{\outward{#1}}
\newcommand{\xrow}[1]{\sembr{#1}} \newcommand{\xval}[1]{\outward{#1}}
\newcommand{\xeff}[1]{\sembr{#1}} \newcommand{\xpre}[1]{\outward{#1}}
\newcommand{\pcomp}[1]{\sembr{#1}} \newcommand{\xrow}[1]{\outward{#1}}
\newcommand{\pval}[1]{\sembr{#1}} \newcommand{\xeff}[1]{\outward{#1}}
\newcommand{\ppre}[1]{\sembr{#1}} \newcommand{\pcomp}[1]{\inward{#1}}
\newcommand{\prow}[1]{\sembr{#1}} \newcommand{\pval}[1]{\inward{#1}}
\newcommand{\peff}[1]{\sembr{#1}} \newcommand{\ppre}[1]{\inward{#1}}
\newcommand{\prow}[1]{\inward{#1}}
\newcommand{\peff}[1]{\inward{#1}}
\newcommand{\eamb}{\ensuremath{E_{\mathsf{amb}}}} \newcommand{\eamb}{\ensuremath{E_{\mathsf{amb}}}}
\newcommand{\trval}[1]{\mathcal{T}\sembr{#1}} \newcommand{\trval}[1]{\mathcal{T}\sembr{#1}}

View File

@@ -6500,18 +6500,27 @@ getting stuck on an unhandled operation.
\subsection{Effect sugar} \subsection{Effect sugar}
\label{sec:effect-sugar} \label{sec:effect-sugar}
Every effect row which share the same effect variable must mention the The row polymorphism formalism underlying the effect system is rigid
exact same operations to be complete. Their presence information may with regard to presence information. Every effect row which share the
differ. In higher-order effectful programming this can lead to same effect variable must mention the exact same operations to be
duplication of information, and thus verbose effect complete, that is they must mention whether an operation is present,
signature. Verbosity can be a real nuisance in larger codebases. absent, or polymorphic in their its presence. Consequently, in
higher-order effectful programming this can cause duplication of
information, which in turn can cause effect signatures to become
overly verbose. In most cases verbosity is undesirable if the extra
information is redundant, and in practice it can be real nuisance in
larger codebases.
% %
We can retrospectively fix this issue with some syntactic sugar rather We can retrospectively fix this issue with some syntactic sugar rather
than redesigning the entire effect system to rectify this problem. than redesigning the entire effect system to rectify this problem.
To this end, I will take inspiration from the effect system of Frank,
which allows eliding redundant information in many
cases~\cite{LindleyMM17}.
% %
I will describe an ad-hoc elaboration scheme, which is designed to In the following I will describe an ad-hoc elaboration scheme for
emulate Frank's effect polymorphic system~\cite{LindleyMM17} for effect rows, which is designed to guess the programmer's intent for
first-order and second-order functions, but might not work so well for first-order and second-order functions, but it might not work so well for
third-order and above. The reason for focusing on first-order and third-order and above. The reason for focusing on first-order and
second-order functions is that many familiar and useful functions are second-order functions is that many familiar and useful functions are
either first-order or second-order, and in the following sections we either first-order or second-order, and in the following sections we
@@ -6521,44 +6530,50 @@ higher order, e.g. in Chapter~\ref{ch:handlers-efficiency} we shall
use third-order functions; for an example of a sixth order function use third-order functions; for an example of a sixth order function
see \citet{Okasaki98}). see \citet{Okasaki98}).
First, let us consider some small instances of the kind of incomplete First, let us consider the familiar second-order function $\map$ for
effect rows we would like to be able to write. Consider the type of lists which is completely parametric in its effects.
$\map$ function for lists.
% %
\[ \[
\map : \Record{\alpha \to \beta;\List~\alpha} \to \List~\beta \ba{@{~}l@{~}l@{}l@{~}l}
&\map : \Record{\alpha \to \beta &;\List~\alpha} \to &\List~\beta\\
\equiv&\map : \Record{\alpha \to \beta \eff \{\varepsilon\}&;\List~\alpha}
\to &\List~\beta \eff \{\varepsilon\}
\ea
\] \]
% %
For this type to be correct in $\HCalc$ (and $\BCalc$ for that matter) For this type to be correct in $\HCalc$ (and $\BCalc$ for that matter)
we must annotate the arrows with their effect signatures, e.g. we must annotate the computation type with their effect row.
% %
\[ These effect annotations do not convey any additional information,
\map : \Record{\alpha \to \beta \eff \{\varepsilon\};\List~\alpha}
\to \List~\beta \eff \{\varepsilon\}
\]
%
The effect annotations do not convey any additional information,
because the function is entirely parametric in all effects, thus the because the function is entirely parametric in all effects, thus the
ink spent on the effect annotations is really wasted in this instance. ink spent on the annotations is really wasted in this instance.
% %
To fix this we simply need to instantiate each computation type with To fix this we simply need to instantiate each computation type with
the same effect variable $\varepsilon$. the same effect variable $\varepsilon$.
A slight variation of this example is an effectful second-order A slightly more interesting example is a second-order function which
function that is parametric in its argument's effects. itself performs some operation, but is otherwise parametric in the
effects of its argument.
% %
\[ \[
(A \to B_1 \eff \{\varepsilon\}) \to B_2 \eff \{\ell : A' \opto B';\varepsilon\} \simeq (A \to B_1 \eff \{\ell : A' \opto B';\varepsilon\}) \to B_2 \eff \{\ell : A' \opto B';\varepsilon\} \ba{@{~}l@{~}l@{}l@{~}l}
&(A \to B_1 \eff \{ &\varepsilon\}) &\to B_2 \eff \{\ell : A' \opto B';\varepsilon\}\\
\equiv&(A \to B_1 \eff \{\ell : A' \opto B';&\varepsilon\}) &\to B_2 \eff \{\ell : A' \opto B';\varepsilon\}
\ea
\] \]
% %
For the effect row of the functional parameter to be correct it must To be type-correct both rows must mention the $\ell$
mention the $\ell$ operation. operation. However, this information is redundant on the functional
parameter. The idea here is to push the information of the ambient
effect row $B_2$ inwards to $B_1$ such that the functional argument
can be granted the ability to perform $\ell$.
% %
The idea here is to push the effect signature inwards. The following The following infix function $\vartriangleleft$ implements the inward
infix function $\vartriangleleft$ copies operations and their presence push of information by copying operations from its right parameter to
information from its right argument to its left argument. its left parameter.
% %
\begin{equations} \[
\ba{@{~}r@{~}c@{~}l}
\vartriangleleft &:& \EffectCat \times \EffectCat \to \EffectCat\\ \vartriangleleft &:& \EffectCat \times \EffectCat \to \EffectCat\\
E \vartriangleleft \{\cdot\} &\defas& E\\ E \vartriangleleft \{\cdot\} &\defas& E\\
E \vartriangleleft \{\varepsilon\} &\defas& E\\ E \vartriangleleft \{\varepsilon\} &\defas& E\\
@@ -6567,30 +6582,36 @@ information from its right argument to its left argument.
\{\ell : P\} \uplus (E \vartriangleleft \{R\}) & \text{if } \ell \notin E\\ \{\ell : P\} \uplus (E \vartriangleleft \{R\}) & \text{if } \ell \notin E\\
\{R\} \vartriangleleft E & \text{otherwise} \{R\} \vartriangleleft E & \text{otherwise}
\end{cases}\\ \end{cases}\\
\end{equations} \ea
\]
% %
This function essentially computes the union of the two effect rows. This function essentially computes the union of the two effect rows.
Another example is an observably pure function whose input is The most frequent case to occur is a second-order function which
effectful, meaning the function must be handling the effects of its handles the effects of its argument.
argument internally (or not apply its argument at all). To capture the
intuition that operations have been handled, we would like to not
mention the handled operations in the function's effect signature,
however, the underlying row polymorphism formalism requires us to
explicitly mention handled operations as being polymorphic in their
presence, e.g.
% %
\[ \[
(A \to B_1 \eff \{\ell : A' \opto B';\varepsilon\}) \to B_2 \eff \{\varepsilon\} \simeq (A \to B_1 \eff \{\ell : A' \opto B';\varepsilon\}) \to B_2 \eff \{\ell : \theta;\varepsilon\} \ba{@{~}l@{~}l@{}l}
& (A \to B_1 \eff \{\ell : A' \opto B';\varepsilon\}) \to B_2 \eff \{ &\varepsilon\}\\
\equiv& (A \to B_1 \eff \{\ell : A' \opto B';\varepsilon\}) \to B_2 \eff \{\ell : \theta;&\varepsilon\}
\ea
\] \]
% %
The idea is to extract the effect signature of the domain $A$ and then To capture the intuition that operations have been handled, we would
copy every operation from this signature into the function's signature like to not mention the handled operations in the effect row attached
as polymorphic in its presence if it is not mentioned by the to $B_2$.
function's signature. The following function implements the copy
aspect of this idea.
% %
\begin{equations} The idea is to propagate the information of the effect row attached to
$B_1$ outwards such that this information can be used to complete the
effect row on $B_2$. To complete the row we need to copy the
operations unique to the effect row of $B_1$ into the effect row of
$B_2$ and instantiate them with a fresh presence variable.
%
The following function $\vartriangleright$ propagates information from
its left parameter to its right parameter.
%
\[
\ba{@{~}r@{~}c@{~}l}
\vartriangleright &:& \EffectCat \times \EffectCat \to \EffectCat\\ \vartriangleright &:& \EffectCat \times \EffectCat \to \EffectCat\\
\{\cdot\} \vartriangleright E &\defas& E\\ \{\cdot\} \vartriangleright E &\defas& E\\
\{\varepsilon\} \vartriangleright E &\defas& E\\ \{\varepsilon\} \vartriangleright E &\defas& E\\
@@ -6604,62 +6625,144 @@ aspect of this idea.
\{\ell : P\} \uplus (\{R\} \vartriangleright E) & \text{if } \ell \notin E\\ \{\ell : P\} \uplus (\{R\} \vartriangleright E) & \text{if } \ell \notin E\\
\{R\} \vartriangleright E & \text{otherwise} \{R\} \vartriangleright E & \text{otherwise}
\end{cases} \end{cases}
\end{equations} \ea
\]
% %
The only subtlety occur in the interesting case which is when an The only subtlety occur in the interesting case which is when an
operation is present in the left signature, but not in the right operation is present in the left row, but not in the right row. In
signature. In this case we instantiate the operation with a fresh this case we instantiate the operation with a fresh presence variable
presence variable in the output signature. in the output row.
Propagation of information in either direction should only happen if
the effect rows share the same effect variable. To avoid erroneous
propagation of information we implement and use the following guarded
variations of $\vartriangleleft$ and $\vartriangleright$.
%
\[
\ba{@{}l@{\qquad}@{}l}
\ba[t]{@{~}r@{~}l@{~}c@{~}l}
\{R;\varepsilon\} \blacktriangleleft &\{R';\varepsilon\} &\defas&
\{R;\varepsilon\} \vartriangleleft \{R';\varepsilon\}\\
\{R;\varepsilon\} \blacktriangleleft &\{R';\varepsilon'\} &\defas&
\{R;\varepsilon\}
\ea &
\ba[t]{@{~}r@{~}l@{~}c@{~}l}
\{R;\varepsilon\} \blacktriangleright &\{R';\varepsilon\} &\defas&
\{R;\varepsilon\} \vartriangleright \{R';\varepsilon\}\\
\{R;\varepsilon\} \blacktriangleright &\{R';\varepsilon'\} &\defas&
\{R';\varepsilon'\}
\ea
\ea
\]
%
The following function $\inward{-}$ pushes the ambient effect row
$\eamb$ inward a given type. I will omit the homomorphic cases as
there is only one interesting case.
% %
\begin{equations} \begin{equations}
\pcomp{-} &:& \CompTypeCat \times \EffectCat \to \CompTypeCat\\
\pcomp{A \eff E}_{\eamb} &\defas& \pval{A}_{\eamb} \eff E \blacktriangleleft \eamb
\end{equations}
%
The following function $\outward{-}$ combines and propagates the
effect rows of a type outward. Again, I omit the homomorphic cases.
%
\[
\ba{@{}l@{\qquad}@{}l}
\ba[t]{@{~}r@{~}c@{~}l}
\xval{-} &:& \ValTypeCat \to \EffectCat\\ \xval{-} &:& \ValTypeCat \to \EffectCat\\
\xval{\alpha} &\defas& \{\cdot\}\\ \xval{\alpha} &\defas& \{\cdot\}\\
\xval{\Record{R}} &\defas& \xval{[R]} \defas \xval{R}\\ \xval{A \to C} &\defas& \xval{A} \blacktriangleright \xcomp{C} \\
\xval{A \to C} &\defas& \xval{A} \vartriangleright \xcomp{C} \\ \ea &
\xval{\forall \alpha^K.C} &\defas& \xcomp{C} \smallskip\\ \ba[t]{@{~}r@{~}c@{~}l}
\xcomp{-} &:& \CompTypeCat \to \EffectCat\\ \xcomp{-} &:& \CompTypeCat \to \EffectCat\\
\xcomp{A \eff E} &\defas& \xval{A} \vartriangleright E \smallskip\\ \xcomp{A \eff E} &\defas& \xval{A} \blacktriangleright E \smallskip\\
\ea \smallskip\\
\ba[t]{@{~}r@{~}c@{~}l}
\xpre{-} &:& \PresenceCat \to \EffectCat\\
% \xpre{\Pre{A}} &\defas& \xval{A}\\
\xpre{\Abs} &\defas& \xpre{\theta} \defas \{\cdot\}
\ea &
\ba[t]{@{~}r@{~}c@{~}l}
\xrow{-} &:& \RowCat \to \Effect\\ \xrow{-} &:& \RowCat \to \Effect\\
\xrow{\cdot} &\defas& \xval{\rho} \defas \{\cdot\}\\ \xrow{\cdot} &\defas& \xval{\rho} \defas \{\cdot\}\\
\xrow{\ell : P;R} &\defas& \xval{P} \vartriangleright \xval{R} \smallskip\\ \xrow{\ell : P;R} &\defas& \xval{P} \blacktriangleright \xval{R}
\xpre{-} &:& \PresenceCat \to \EffectCat\\ \ea
\xpre{\Pre{A}} &\defas& \xval{A}\\ \ea
\xpre{\Abs} &\defas& \xpre{\theta} \defas \{\cdot\} \]
\end{equations} % \[
% \ba{@{}l@{\qquad}@{}l}
% \ba[t]{@{~}r@{~}c@{~}l}
% \xval{-} &:& \ValTypeCat \to \EffectCat\\
% \xval{\alpha} &\defas& \{\cdot\}\\
% \xval{\Record{R}} &\defas& \xval{[R]} \defas \xval{R}\\
% \xval{A \to C} &\defas& \xval{A} \blacktriangleright \xcomp{C} \\
% \xval{\forall \alpha^K.C} &\defas& \xcomp{C}
% \ea &
% \ba[t]{@{~}r@{~}c@{~}l}
% \xcomp{-} &:& \CompTypeCat \to \EffectCat\\
% \xcomp{A \eff E} &\defas& \xval{A} \blacktriangleright E \smallskip\\
% \xrow{-} &:& \RowCat \to \Effect\\
% \xrow{\cdot} &\defas& \xval{\rho} \defas \{\cdot\}\\
% \xrow{\ell : P;R} &\defas& \xval{P} \blacktriangleright \xval{R}
% \ea\\
% \multicolumn{2}{c}{
% \ba{@{~}r@{~}c@{~}l}
% \xpre{-} &:& \PresenceCat \to \EffectCat\\
% \xpre{\Pre{A}} &\defas& \xval{A}\\
% \xpre{\Abs} &\defas& \xpre{\theta} \defas \{\cdot\}
% \ea}
% \ea
% \]
% %
\begin{equations} % \[
\pval{-} &:& \ValTypeCat \times \EffectCat \to \ValTypeCat\\ % \ba{@{}l@{\qquad}@{}l}
\pval{\alpha}_{\eamb} &\defas& \alpha\\ % \ba[t]{@{~}r@{~}c@{~}l}
\pval{\Record{R}}_{\eamb} &\defas& \Record{\prow{R}_{\eamb}}\\ % \pval{-} &:& \ValTypeCat \times \EffectCat \to \ValTypeCat\\
\pval{[R]}_{\eamb} &\defas& [\prow{R}_{\eamb}]\\ % \pval{\alpha}_{\eamb} &\defas& \alpha\\
\pval{A \to C}_{\eamb} &\defas& \pval{A}_{\eamb} \to \pcomp{C}_{\eamb} \\ % \pval{\Record{R}}_{\eamb} &\defas& \Record{\prow{R}_{\eamb}}\\
\pval{\forall \alpha^K.C}_{\eamb} &\defas& \forall\alpha^K.\pcomp{C} \smallskip\\ % \pval{[R]}_{\eamb} &\defas& [\prow{R}_{\eamb}]\\
\pcomp{-} &:& \CompTypeCat \times \EffectCat \to \CompTypeCat\\ % \pval{A \to C}_{\eamb} &\defas& \pval{A}_{\eamb} \to \pcomp{C}_{\eamb} \\
\pcomp{A \eff E}_{\eamb} &\defas& \pval{A}_{\eamb} \eff E \vartriangleleft \eamb \smallskip\\ % \pval{\forall \alpha^K.C}_{\eamb} &\defas& \forall\alpha^K.\pcomp{C}
\prow{-} &:& \RowCat \times \EffectCat \to \RowCat\\ % \ea &
\prow{\cdot}_{\eamb} &\defas& \{\cdot\}\\ % \ba[t]{@{~}r@{~}c@{~}l}
\prow{\rho}_{\eamb} &\defas& \{\rho\}\\ % \pcomp{-} &:& \CompTypeCat \times \EffectCat \to \CompTypeCat\\
\prow{\ell : P;R}_{\eamb} &\defas& \ppre{P};\prow{R} \smallskip\\ % \pcomp{A \eff E}_{\eamb} &\defas& \pval{A}_{\eamb} \eff E \blacktriangleleft \eamb \smallskip\\
\ppre{-} &:& \PresenceCat \times \EffectCat \to \EffectCat\\ % \prow{-} &:& \RowCat \times \EffectCat \to \RowCat\\
\ppre{\Pre{A}}_{\eamb} &\defas& \pval{A}_{\eamb}\\ % \prow{\cdot}_{\eamb} &\defas& \{\cdot\}\\
\ppre{\Abs}_{\eamb} &\defas& \Abs\\ % \prow{\rho}_{\eamb} &\defas& \{\rho\}\\
\ppre{\theta} &\defas& \theta % \prow{\ell : P;R}_{\eamb} &\defas& \ppre{P};\prow{R}
\end{equations} % \ea\\
% \multicolumn{2}{c}{
% \ba[t]{@{~}r@{~}c@{~}l}
% \ppre{-} &:& \PresenceCat \times \EffectCat \to \EffectCat\\
% \ppre{\Pre{A}}_{\eamb} &\defas& \pval{A}_{\eamb}\\
% \ppre{\Abs}_{\eamb} &\defas& \Abs\\
% \ppre{\theta} &\defas& \theta
% \ea}
% \ea
% \]
%
We combine all of the above functions to implement the effect row
elaboration for top-level function types.
% %
\begin{equations} \begin{equations}
\trval{-} &:& \ValTypeCat \to \ValTypeCat\\ \trval{-} &:& \ValTypeCat \to \ValTypeCat\\
\trval{A \to B \eff E} &\defas& \pval{A}_{E'} \to \pval{B}_{E'} \eff E' \vartriangleright E\\ \trval{A \to B \eff E} &\defas& \pval{A}_{E'} \to \pval{B}_{E'} \eff E'\\
\multicolumn{3}{l}{\quad\where~E' = \xval{A} \vartriangleright \xval{B}} \multicolumn{3}{l}{\quad\where~E' = (\xval{A} \blacktriangleright E) \blacktriangleright (\xval{B} \blacktriangleright E)}
\end{equations} \end{equations}
% \begin{equations} %
% \trval{-} &:& \ValTypeCat \to \ValTypeCat\\ The function $\trval{-}$ traverses the abstract syntax of its argument
% \trval{A \to B \eff E} &\defas& A' \to B' \eff E'\\ twice. The first traversal propagates effect information outwards to
% \multicolumn{3}{l}{\quad\where~(A', E_A) \defas \trval{A}_{E}\;\keyw{and}\;(B', \_) = \trval{B}_{\{\varepsilon\}}\;\keyw{and}\; E' = E_A \vartriangleright E} \medskip\\ the ambient effect row $E$. The second traversal pushes the full
% \trval{-} &:& \ValTypeCat \times \EffectCat \to \ValTypeCat \times \EffectCat\\ ambient information $E'$ inwards.
% \trval{\UnitType}_{E_{amb}} &\defas& (\UnitType, \{\cdot\})\\ %
% \trval{A \to B \eff E}_{E_{amb}} &\defas& (A' \to B' \eff E', E_A \vartriangleright E)\\ As a remark, note that the function $\trval{-}$ do not have to
% \multicolumn{3}{l}{\quad\where~E' = (E_A \vartriangleright E) \vartriangleleft E_{amb}\;\keyw{and}\;(A', E_A) = \trval{A}_{E'}\;\keyw{and}\;(B', \_) = \trval{B}_{\{\varepsilon\}}} consider handler types, because they cannot appear at the top-level in
% \end{equations} $\HCalc$. With this syntactic sugar in place we can program with
second-order effectful functions without having to write down
redundant information.
\section{Composing \UNIX{} with effect handlers} \section{Composing \UNIX{} with effect handlers}
\label{sec:deep-handlers-in-action} \label{sec:deep-handlers-in-action}