Ok, so I have figured out how to implement Reader
(and ReaderT
, not shown) using the operational
package:
{-# LANGUAGE GADTs, ScopedTypeVariables #-}
import Control.Monad.Operational
data ReaderI r a where
Ask :: ReaderI r r
type Reader r a = Program (ReaderI r) a
ask :: Reader r r
ask = singleton Ask
runReader :: forall r a. Reader r a -> r -> a
runReader = interpretWithMonad evalI
where evalI :: forall b. ReaderI r b -> (r -> b)
evalI Ask = id
But I can't figure out for my life how to do this with free monads (I'm using Edward Kmett's free
package). The closest I've gotten is this, which I understand is cheating (something about how ((->) r)
is already a monad):
import Control.Monad.Free
type Reader r a = Free ((->) r) a
ask :: Reader r r
ask = Free Pure
runReader :: Reader r a -> r -> a
runReader (Pure a) _ = a
runReader (Free k) r = runReader (k r) r
-- Or, more simply and tellingly:
--
-- > runReader = retract
Even if this wasn't as dumb as I suspect it is, it's not what I want because what I want, basically, is to be able to inspect a Reader
as data...
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 theReader
monad, we can try an applicative version ofoperational
:The structure of a
ProgramA (ReaderI r) a)
can be inspected more straightforwardly than eitherProgram (ReaderI r) a
orFree ((->) r) a
.I don't think it can be done except they way you have. But, I don't think this is unique to reader. Consider the free monad version of writer
obviously,
WriterF
is isomorphic to writer, but this does behave the way we would expect with the simple algebrathus
Similarly, I think of the Free reader as
I like this, because adding them gives you the state monad
Note, that your operational solution could be made to work with
Free
by using the "free functor", as can anything that works with operationalbut, that
FreeFunctor ReaderI
is also isomorphic to(->)
.