module Control.Monad.Operational where
import Control.Monad
import Control.Applicative
data CoYoneda f x where
CoYoneda :: (b -> x) -> f b -> CoYoneda f x
instance Functor (CoYoneda f) where
fmap f (CoYoneda g v) = CoYoneda (f . g) v
liftCoYoneda :: f a -> CoYoneda f a
liftCoYoneda = CoYoneda id
data Free f r = Free (f (Free f r)) | Pure r
instance Functor f => Functor (Free f) where
fmap f x = x >>= return . f
instance Functor f => Applicative (Free f) where
pure = return
(<*>) = ap
instance Functor f => Monad (Free f) where
return = Pure
Free x >>= f = Free $ fmap (>>= f) x
Pure x >>= f = f x
liftF :: Functor f => f r -> Free f r
liftF cmd = Free (fmap Pure cmd)
newtype Program f a = Program { toFree :: Free (CoYoneda f) a }
instance Functor (Program f) where
fmap f = Program . fmap f . toFree
instance Applicative (Program f) where
pure = return
(<*>) = ap
instance Monad (Program f) where
return = Program . return
x >>= f = Program $ toFree x >>= toFree . f
singleton :: f a -> Program f a
singleton = Program . liftF . liftCoYoneda
interpret :: forall instr m b. Monad m => (forall a. instr a -> m a) -> Program instr b -> m b
interpret g (Program (Free (CoYoneda f x))) = g x >>= interpret g . Program . f
interpret _ (Program (Pure a)) = return a