While writing my Haskell BitTorrent tracker, I came up with something I hadn’t seen before, but I assumed must be obvious: ContEitherT :

newtype ContEitherT l m r = ContEitherT :: { runContEitherT :: forall z. (l -> m z) -> (r -> m z) -> m z } left :: l -> ContEitherT l m r left l = ContEitherT $ \lk rk -> lk l right :: r -> ContEitherT l m r right r = ContEitherT $ \lk rk -> rk r liftEither :: Either l r -> ContEither l m r liftEither (Left l) = left l liftEither (Right r) = right r

The reasoning I had was that if I had a long chain of computations that all consume and produce either values, that it may be better to try and bail out early rather than repeatedly inspecting the tagged value. It turns out that this is just church encoding, but it’s fun.

Of course, this type is a monad and a functor without any help from m:

-- | ContEitherT is really a bifunctor, but we only need plain functor on -- the right in this case. Should look similar to the functor instance for -- ContT instance Functor (ContEitherT l m) where fmap f (ContEitherT kka) = ContEitherT $ \lk rk -> kka lk (rk . f) -- | ContEitherT is an applicative. instance Applicative (ContEitherT l m) where pure = right (ContEitherT kkf) (ContEitherT kka) = ContEitherT $ \lk rk -> kkf lk (\f -> kka lk (rk . f)) (ContEitherT kka) *> (ContEitherT kkb) = ContEitherT $ \lk rk -> kka lk (\_ -> kkb lk rk) (ContEitherT kka) kka lk (\a -> kkb lk (rk . const a)) -- | ContEitherT is a monad on the right. Note that the failure propogates -- exactly like it would for either, but without needing to be inspected. instance Monad (ContEitherT l m) where return = right (ContEitherT kka) >>= f = ContEitherT $ \lk rk -> kka lk (\a -> runContEitherT (f a) lk rk)

We know how to lift, what about lowering? Lowering when m is the identity functor is easy:

lowerEitherIdent :: ContEitherT l Identity r -> Either l r lowerEitherIdent x = runIdentity $ runContEitherT x (Identity . Left) (Identity . Right)

What about if M is an arbitrary monad? Do we need an monad, or just an applicative? All we really need is for m to be an applicative (or more strictly just a pointed functor:

lowerEither :: (Functor m) => ContEitherT l m r -> m (Either l r) lowerEither x = runContEitherT x (pure . Left) (pure . Right)

Note that the return type is m (Either l r) meaning that we have to invoke the effects in order to decide on the result. This is as it should be. The code might read a file and call the continuations based on the result.

What about catch? or throw?

-- | Catch for ContEitherT. -- Note the similarity to (>>=) above. eCatch :: ContEitherT l m r -> (l -> ContEitherT l m r) -> ContEitherT l m r eCatch ka handler = ContEitherT $ \lk rk -> runContEitherT ka (\l -> runContEitherT (handler l) lk rk) rk -- | Throw for ContEitherT. -- Left analogue of pure eThrow :: l -> ContEitherT l m r ethrow l = ContEitherT $ \lk rk -> lk l -- | Monadic Throw for ContEitherT -- Left analogue of lift eThrowM :: (m l) -> ContEitherT l m r eThrowM ml = ContEitherT $ \lk rk -> ml >>= lk

It looks like what we have is a bi-functor that is a monad in either argument holding the other constant. There’s probably a nice way to write things in it so that you can chain together both successes and failures. I know there is previous work on this.