\begin{comment} \begin{code} {-# LANGUAGE GADTs #-} module LiveCoding.CellExcept where -- base import Control.Monad import Data.Data import Data.Void -- transformers import Control.Monad.Trans.Except -- essenceoflivecoding import LiveCoding.Cell import LiveCoding.Exceptions import LiveCoding.Exceptions.Finite \end{code} \end{comment} We can save on boiler plate by dropping the Coyoneda embedding for an ``operational'' monad: \fxerror{Cite operational} \fxerror{Move the following code into appendix?} \begin{code} data CellExcept m a b e where Return :: e -> CellExcept m a b e Bind :: CellExcept m a b e1 -> (e1 -> CellExcept m a b e2) -> CellExcept m a b e2 Try :: (Data e, Finite e) => Cell (ExceptT e m) a b -> CellExcept m a b e \end{code} \begin{comment} \begin{code} instance Monad m => Functor (CellExcept m a b) where fmap = liftM instance Monad m => Applicative (CellExcept m a b) where pure = return (<*>) = ap \end{code} \end{comment} The \mintinline{haskell}{Monad} instance is now trivial: \begin{code} instance Monad m => Monad (CellExcept m a b) where return = Return (>>=) = Bind \end{code} As is typical for operational monads, all of the effort now goes into the interpretation function: \begin{code} runCellExcept :: Monad m => CellExcept m a b e -> Cell (ExceptT e m) a b \end{code} \begin{spec} runCellExcept (Bind (Try cell) g) = cell >>>= commute (runCellExcept . g) runCellExcept ... = ... \end{spec} \begin{comment} \begin{code} runCellExcept (Return e) = constM $ throwE e runCellExcept (Try cell) = cell runCellExcept (Bind (Try cell) g) = cell >>>== commute (runCellExcept . g) runCellExcept (Bind (Return e) f) = runCellExcept $ f e runCellExcept (Bind (Bind ce f) g) = runCellExcept $ Bind ce $ \e -> Bind (f e) g \end{code} \end{comment} As a slight restriction of the framework, throwing exceptions is now only allowed for finite types: \begin{code} try :: (Data e, Finite e) => Cell (ExceptT e m) a b -> CellExcept m a b e try = Try \end{code} In practice however, this is less often a limitation than first assumed, since in the monad context, calculations with all types are allowed again. \fxerror{But the trouble remains that builtin types like Int and Double can't be thrown.} \fxfatal{The rest is explained in the main article differently. Merge.} \begin{comment} \begin{code} safely :: Monad m => CellExcept m a b Void -> Cell m a b safely = hoistCell discardVoid . runCellExcept discardVoid :: Functor m => ExceptT Void m a -> m a discardVoid = fmap (either absurd id) . runExceptT safe :: Monad m => Cell m a b -> CellExcept m a b Void safe cell = try $ liftCell cell \end{code} \end{comment}