\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 -- mmorph import Control.Monad.Morph -- essence-of-live-coding 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 a b m e where Return :: e -> CellExcept a b m e Bind :: CellExcept a b m e1 -> (e1 -> CellExcept a b m e2) -> CellExcept a b m e2 Try :: (Data e, Finite e) => Cell (ExceptT e m) a b -> CellExcept a b m e \end{code} \begin{comment} \begin{code} instance Monad m => Functor (CellExcept a b m) where fmap = liftM instance Monad m => Applicative (CellExcept a b m) where pure = return (<*>) = ap instance MFunctor (CellExcept a b) where hoist morphism (Return e) = Return e hoist morphism (Bind action cont) = Bind (hoist morphism action) (hoist morphism . cont) hoist morphism (Try cell) = Try $ hoistCell (mapExceptT morphism) cell \end{code} \end{comment} The \mintinline{haskell}{Monad} instance is now trivial: \begin{code} instance Monad m => Monad (CellExcept a b m) 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 a b m 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 a b m 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 a b m 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 a b m Void safe cell = try $ liftCell cell -- | Run a monadic action and immediately raise its result as an exception. once :: (Monad m, Data e, Finite e) => (a -> m e) -> CellExcept a arbitrary m e once kleisli = try $ arrM $ ExceptT . (Left <$>) . kleisli -- | Like 'once', but the action does not have an input. once_ :: (Monad m, Data e, Finite e) => m e -> CellExcept a arbitrary m e once_ = once . const \end{code} \end{comment}