Here's how I'd implement this. First, if you want Writer
to be strictly more powerful than Reader
than we'd want a function
liftReader :: FooReader a -> FooWriter a
liftReader (FooReader a) = FooWriter a
This works because they're structurally equivalent and their monad instances should be isomorphic.
Then we can just
t :: FooWriter Int
t = liftReader getVal
If you want to be able to go the other way, it's pretty easy
liftWriter :: FooWriter a -> FooReader a
liftWriter (FooWriter a) = FooReader a
Now the fact that we can lift these types into each other makes you think that they're somehow equivalent.. And in fact they are, you basically have
import Control.Monad.State
newtype FooReader s a = FooReader (State s a)
newtype FooWriter s a = FooWriter (State s a)
And State
provides get
and put
which are analogous to your getVal
and writeFunc