packages feed

essence-of-live-coding-0.2.6: 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{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}