问题
Suppose I have an IO Int
wrapped in a StateT MyState
, then I have a value of State MyState Int
which I want to use in the stacked monad. How do I lift it in this inner sense? I already know to use lift
or liftIO
if I get something compatible with the inside that I just need to lift to the outside monad, but now I have the opposite problem: the value is already in the outer monad but not the inner one.
For instance:
checkSame :: State MyState a -> IO a -> StateT MyState IO Bool
checkSame sim real = do
rres <- liftIO real
sres <- ??? sim
return $ rres == sres
Do I have to 'get' the state, shove it through runState by hand and box it all up again, or is there some generic way to do this?
BTW, that sim parameter is a whole bunch of stateful functions that have nothing to do with IO, so I'm a bit reluctant to make them all return StateT MyState IO a
if I can avoid it.
回答1:
You have two options:
- Find a monad morphism. This is often a matter of finding the right library; in this case hoist and generalize together should get you where you need to go.
Make your
State
action more polymorphic. This is the commonly used one, and the recommended one; it amounts to pre-applying the morphism from part 1, but has a lot of machinery put in place already in themtl
library to make it easy. The idea here is that if you write yourState
action just in terms ofget
,put
, andmodify
, then instead of the typeState s a
, you can give it the type:MonadState s m => m a
Then later, at the call site, you can choose whatever monad is appropriate for this, including both
State s a
andStateT s IO a
. Moreover, since it specializes to the typeState s a
, you can be sure it doesn't do anyIO
or anything like that thatState s a
itself couldn't do, so you get the same behavioral guarantees.
来源:https://stackoverflow.com/questions/27200132/lift-to-fix-the-inside-of-a-monad-transformer-stack