Per Gabriel's suggestion, the alternate implementation.
data Step a b x r
= Done x
| Yield b r
| Await (a -> r)
| Fail
instance Functor (Step a b x) where
fmap g s = case s of
Done x -> Done x
Yield b r -> Yield b (g r)
Await k -> Await $ g . k
Fail -> Fail
-- | note the monad type needs to parameterize over type of `x` in `Done x`
data CoroutineT a b m x = CoT { resume :: m (Step a b x (CoroutineT a b m x)) }
instance Monad m => Functor (CoroutineT a b m) where
fmap g (CoT m) = CoT $ liftM ap m where
ap (Done x) = Done $ g x
ap (Yield b r) = Yield b (fmap g r)
ap (Await k) = Await $ (fmap g) . k
ap Fail = Fail
instance Monad m => Monad (CoroutineT a b m) where
return = CoT . return . Done
(CoT m) >>= g = CoT $ m >>= \step -> case step of
Done x -> resume $ g x
Yield b r -> return . Yield b $ r >>= g
Await k -> return . Await $ (>>=g) . k
Fail -> return Fail
Note the implementation of return
makes more sense now as well.