essence-of-live-coding-0.2.7: src/LiveCoding/Preliminary/CellExcept/Applicative.lhs
\begin{comment}
\begin{code}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TupleSections #-}
module LiveCoding.Preliminary.CellExcept.Applicative where
-- base
import Data.Data
-- transformers
import Control.Monad.Trans.Except
import Control.Monad.Trans.Reader
-- essence-of-live-coding
import LiveCoding.Cell
import LiveCoding.Exceptions
\end{code}
\end{comment}
\paragraph{Applying it to \mintinline{haskell}{Applicative}}
If we are allowed to read the first exception during the execution of the second cell,
we can simply re-raise it once the second exception is thrown:
\begin{code}
andThen
:: (Data e1, Monad m)
=> Cell (ExceptT e1 m) a b
-> Cell (ExceptT e2 m) a b
-> Cell (ExceptT (e1, e2) m) a b
cell1 `andThen` Cell { .. } = cell1 >>>= Cell
{ cellStep = \state (e1, a) ->
withExceptT (e1, ) $ cellStep state a
, ..
}
\end{code}
\begin{comment}
\begin{code}
cell1 `andThen` cell2 = cell1 `andThen` toCell cell2
\end{code}
\begin{spec}
hoistCell readException cell2
where
readException
:: Functor m
=> ExceptT e2 m x
-> ReaderT e1 (ExceptT(e1, e2) m) x
readException exception = ReaderT
$ \e1 -> withExceptT (e1, ) exception
\end{spec}
\end{comment}
Given two \mintinline{haskell}{Cell}s,
the first may throw an exception,
upon which the second cell gains control.
As soon as it throws a second exception,
both exceptions are thrown as a tuple.
At this point, we unfortunately have to give up the efficient \mintinline{haskell}{newtype}.
The spoilsport is, again the type class \mintinline{haskell}{Data},
to which the exception type \mintinline{haskell}{e1} is subjected
(since the exception must be stored during the execution of the second cell).
But the issue is minor,
it is fixed by defining the \emph{free functor},
or \emph{Co-Yoneda construction}:
\fxwarning{Maybe cite http://comonad.com/reader/2016/adjoint-triples/ or search something else}
\fxwarning{Possible other names: Mode}
\begin{code}
data CellExcept a b m e = forall e' .
Data e' => CellExcept
{ fmapExcept :: e' -> e
, cellExcept :: Cell (ExceptT e' m) a b
}
\end{code}
While ensuring that we only store cells with exceptions that can be \emph{bound},
we do not restrict the parameter type \mintinline{haskell}{e}.
It is known that this construction gives rise to a \mintinline{haskell}{Functor} instance for free:
\begin{code}
instance Functor (CellExcept a b m) where
fmap f CellExcept { .. } = CellExcept
{ fmapExcept = f . fmapExcept
, ..
}
\end{code}
The \mintinline{haskell}{Applicative} instance arises from the work we have done so far.
\mintinline{haskell}{pure} is implemented by throwing a unit and transforming it to the required exception,
while sequential application is a bookkeeping exercise around the previously defined function \mintinline{haskell}{andThen}:
\begin{code}
instance Monad m
=> Applicative (CellExcept a b m) where
pure e = CellExcept
{ fmapExcept = const e
, cellExcept = constM $ throwE ()
}
CellExcept fmap1 cell1 <*>
CellExcept fmap2 cell2 = CellExcept { .. }
where
fmapExcept (e1, e2) = fmap1 e1
$ fmap2 e2
cellExcept = cell1 `andThen` cell2
\end{code}