Ok, so I have figured out how to implement Reader
(and ReaderT
, not shown) using the operational package:
{-# LANGUAGE GADTs, ScopedTyp
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
data WriterF m a = WriterF m a deriving (Functor)
type Writer m = Free (WriterF m)
obviously, WriterF
is isomorphic to writer, but this does behave the way we would expect with the simple algebra
algebraWriter :: Monoid m => WriterF m (m,a) -> (m,a)
algebraWriter (WriterF m1 (m2,a)) = (m1 <> m2,a)
thus
runWriter :: Monoid m => Writer m a -> (m,a)
runWriter (Pure a) = (mempty,a)
runWriter (Free x) = algebraWriter . fmap runWriter $ x
Similarly, I think of the Free reader as
type ReaderF r = (->) r
type Reader r = Free (ReaderF r)
I like this, because adding them gives you the state monad
type State x = Free ((ReaderF x) :+: (WriterF x))
runState :: State x a -> x -> (a,x)
runState (Pure a) x = (a,x)
runState (Free (Inl f)) x = runState (f x) x
runState (Free (Inr (WriterF x f))) _ = runState f x
Note, that your operational solution could be made to work with Free
by using the "free functor", as can anything that works with operational
data FreeFunctor f x = forall a. FreeFunctor (f a) (a -> x)
but, that FreeFunctor ReaderI
is also isomorphic to (->)
.