The definition you give of SCont
is weird to me: how comes the successful continuation takes the failure one as an argument? Plus, given you return
and bind
, it looks like the failure one is threaded all the way down the call tree and... applied to the success one in case you hit a return.
I would expect the 'double barrelled continuation Monad' to be defined like so:
module SFCont where
import Control.Monad
newtype SFCont r a = SFCont { sfCont :: SCont r a -> FCont r a -> r }
type SCont r a = a -> r
type FCont r a = r
instance Functor (SFCont r) where
fmap f (SFCont c) = SFCont $ \ ks -> c (ks . f)
-- this will be obtained from the definition of the `Monad` instance
instance Applicative (SFCont r) where
pure = return
(<*>) = ap
instance Monad (SFCont r) where
return a = SFCont $ const . ($ a)
m >>= f = SFCont $ \ ks kf -> sfCont m (\ a -> sfCont (f a) ks kf) kf
We can find a systematic way of defining it (and its cousin where failure can take an element of an error type e
) by reasoning up to isomorphism and noticing that:
SCont r a -> FCont r a -> r ~{ unfolding SCont, FCont }
(a -> r) -> r -> r ~{ r iso (() -> r) }
(a -> r) -> (() -> r) -> r ~{ uncurrying }
((a -> r), (() -> r)) -> r ~{ ((a -> r), (b -> r)) iso (Either a b -> r) }
(Either a () -> r) -> r ~{ Either a () iso Maybe a }
(Maybe a -> r) -> r ~{ folding MaybeT, Cont }
MaybeT (Cont r) a
And now, we can leverage existing instances by writing down this iso:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module SFCont where
import Data.Functor.Identity
import Control.Monad
import Control.Monad.Trans.Maybe
import Control.Monad.Trans.Cont
newtype SFCont r a = SFCont { sfCont :: SCont r a -> FCont r a -> r }
type SCont r a = a -> r
type FCont r a = r
newtype SFCont' r a = SFCont' { sfCont' :: MaybeT (Cont r) a }
deriving (Functor, Applicative, Monad)
to :: SFCont r a -> SFCont' r a
to m = SFCont' $ MaybeT $ ContT $ \ k -> Identity $
sfCont m (runIdentity . k . Just) (runIdentity $ k Nothing)
from :: SFCont' r a -> SFCont r a
from m = SFCont $ \ ks kf -> runIdentity $
(runContT $ runMaybeT $ sfCont' m) (Identity . maybe kf ks)
instance Monad (SFCont r) where
return = from . return
m >>= f = from $ to m >>= to . f
In the case where the failure continuation could take an element of an error type e
, you'd follow the same process and arrive at the stack EitherT e (Cont r) a
for your iso.