|
|
|
@ -468,7 +468,84 @@ unpacking place corresponds to the place, in the previous style, where
|
|
|
|
|
the variable would have been first named. Unpacking is a fair price to
|
|
|
|
|
pay to recover readable code.
|
|
|
|
|
|
|
|
|
|
% Acknolwedgements: François Pottier.
|
|
|
|
|
\subsection{Another example} To conclude, we would like to demonstrate
|
|
|
|
|
the generality of this approach to a wider family of applicative
|
|
|
|
|
functions with ``quantifier''-like combinators, by briefly showcasing
|
|
|
|
|
the same refactoring in a different applicative functor -- an
|
|
|
|
|
artificial example.
|
|
|
|
|
|
|
|
|
|
Consider an applicative \lstinline{'a cloud} for ``computations in the
|
|
|
|
|
cloud'', where operating on a resources (here a remote file) comes
|
|
|
|
|
with a ``cost'' (API usage, time, money, or something else) that is
|
|
|
|
|
returned to the programmer when they conclude using the resource. In
|
|
|
|
|
this model, opening a (remote) file for reading could be exposed by
|
|
|
|
|
the following quantifier-like operation:
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
val with_input : path -> (in_channel -> 'r cloud) -> (cost * 'r) cloud
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
|
|
and now the user desires to generalize this into a function that opens
|
|
|
|
|
a list of paths, operates on the correspond list of input channels,
|
|
|
|
|
and returns the sum of all costs (we will assume that
|
|
|
|
|
\lstinline{type cost = int} for simplicity). They want to implement:
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
val with_inputs : path list -> (in_channel list -> 'r cloud) -> (cost * 'r) cloud
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
|
|
and this is tricky. Our proposal, ``turning direct outputs into modal
|
|
|
|
|
inputs'', suggests the following change to the primitive operation,
|
|
|
|
|
introducing a \lstinline{binder} type that marks the place where
|
|
|
|
|
continuation-passing-style should be used:
|
|
|
|
|
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
type ('a, 'r) binder = ('a -> 'r cloud) -> 'r cloud
|
|
|
|
|
val with_input : (in_channel * cost cloud, 'r) binder
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
|
|
|
|
|
An implementation of \lstinline{with_inputs} using the original API is on the left,
|
|
|
|
|
and our proposed approach is on the right.
|
|
|
|
|
|
|
|
|
|
\hspace{-3em}
|
|
|
|
|
\begin{minipage}{0.45\linewidth}
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
let rec with_inputs paths k =
|
|
|
|
|
match paths with
|
|
|
|
|
| [] ->
|
|
|
|
|
let+ r = k [] in (0, r)
|
|
|
|
|
| p :: ps ->
|
|
|
|
|
let+ (cost_p, (cost_ps, r)) =
|
|
|
|
|
let@ chan = with_input p in
|
|
|
|
|
let@ chans = with_inputs pts in
|
|
|
|
|
k (chan :: chans)
|
|
|
|
|
in (cost_p + cost_ps, r)
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
\end{minipage}
|
|
|
|
|
\hfill
|
|
|
|
|
\begin{minipage}{0.55\linewidth}
|
|
|
|
|
\begin{lstlisting}
|
|
|
|
|
let rec with_inputs paths =
|
|
|
|
|
fun k -> match paths with
|
|
|
|
|
| [] -> k ([], pure 0)
|
|
|
|
|
| p :: ps ->
|
|
|
|
|
let@ chan, cost_p = with_input p in
|
|
|
|
|
let@ chans, cost_ps = with_input ps in
|
|
|
|
|
let+ res = k (p :: ps)
|
|
|
|
|
and+ cost_p = cost_p
|
|
|
|
|
and+ cost_ps = cost_ps
|
|
|
|
|
in (cost_p + cost_ps, res)
|
|
|
|
|
\end{lstlisting}
|
|
|
|
|
\end{minipage}
|
|
|
|
|
|
|
|
|
|
Notice, again, that the original API pushes the cost ``on the stack'',
|
|
|
|
|
and that the variables \lstinline{cost_p}, \lstinline{cost_ps} are
|
|
|
|
|
bound far from the place that logically introduces them in the left
|
|
|
|
|
solution. The issue is solved on the right, at the cost of explicit
|
|
|
|
|
unpacking of the modal inputs.
|
|
|
|
|
|
|
|
|
|
\subsection*{Acknowledgments}
|
|
|
|
|
We wish to thank François Pottier and our anonymous reviewers for their feedback.
|
|
|
|
|
|
|
|
|
|
\bibliography{biblio}
|
|
|
|
|
\end{document}
|
|
|
|
|