Question

I currently have two monads who share the same types, implemented similar to a State monad:

newtype FooRead  a = FooRead  { runFooRead  :: Context -> (a,Context) }
newtype FooWrite a = FooWrite { runFooWrite :: Context -> (a,Context) }

The difference between them is that the first only allows reading of the context (bind will not change it), while the second allows editing the context as well.

There are then functions who use the context in FooRead to compute some value without changing the state of the context:

getVal :: FooRead a
getVal = do x <- ...
            return x

Now I want to execute one of these reading functions from code in the writer monad:

writeFunc :: FooWrite ()
writeFunc = do x <- liftVal getVal
            ...

Where liftVal :: FooRead a -> FooWrite a is a function that extracts the value returned by the FooRead function and rolls it into a FooWrite monad. This is all good.

However, I can not figure out a way to roll the execution of getValin the above into the context from my FooWrite monad. With the above implementation, getVal will be run in an empty instance of the monad.

I can figure out how to construct a FooRead instance using a FooWrite context as

lower :: FooWrite a -> FooRead a

Essentially I want to demote my writer to a reader, execute code within the reader, the re-promote it to a writer.

But not how to actually execute code within this monad?

Was it helpful?

Solution

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top