Ok, so I have figured out how to implement Reader
(and ReaderT
, not shown) using the operational package:
{-# LANGUAGE GADTs, ScopedTyp
Well, I've been looking at this for 3 hours now, and I think I found something I like better. Since the Reader
applicative is the same as the Reader
monad, we can try an applicative version of operational
:
{-# LANGUAGE RankNTypes, GADTs, FlexibleInstances #-}
import Control.Applicative
data ProgramA instr a where
Pure :: a -> ProgramA r a
Ap :: ProgramA r (a -> b) -> ProgramA r a -> ProgramA r b
Instr :: instr a -> ProgramA instr a
infixl `Ap`
instance Functor (ProgramA instr) where
fmap f (Pure a) = Pure (f a)
fmap f (ff `Ap` fa) = ((f .) <$> ff) `Ap` fa
fmap f instr = Pure f `Ap` instr
instance Applicative (ProgramA instr) where
pure = Pure
(<*>) = Ap
interpretA :: Applicative f =>
(forall a. instr a -> f a)
-> ProgramA instr a
-> f a
interpretA evalI (Pure a) = pure a
interpretA evalI (ff `Ap` fa) = interpretA evalI ff <*> interpretA evalI fa
interpretA evalI (Instr i) = evalI i
data ReaderI r a where
Ask :: ReaderI r r
type Reader r a = ProgramA (ReaderI r) a
ask :: Reader r r
ask = Instr Ask
runReader :: Reader r a -> r -> a
runReader = interpretA (\Ask -> id)
instance Monad (ProgramA (ReaderI r)) where
return = pure
ma >>= f = runReader <$> fmap f ma <*> ask
The structure of a ProgramA (ReaderI r) a)
can be inspected more straightforwardly than either Program (ReaderI r) a
or Free ((->) r) a
.