How to combine two different monads

拟墨画扇 提交于 2020-01-04 05:21:20

问题


I'm testing a REST server. I hit it in the IO monad and simulate it in State Db where Db tracks the supposed state of the server. The following function is supposed to run both versions and compare the results...

check :: (Eq a, MonadState d s) => s a -> IO a -> s (IO Bool)
-- or: check :: (Eq a, MonadState d s, MonadIO i) => s a -> i a -> s (i Bool)
check _ _ = (return.return) False -- for now

but when I try it with these simplest possible functions ...

simReset :: State Db ()
realReset :: IO ()

reset :: StateT Db IO Bool
reset = check simReset realReset

I get this error:

Couldn't match expected type `Bool' with actual type `IO Bool'
Expected type: StateT Db IO Bool
  Actual type: StateT Db IO (IO Bool)
In the return type of a call of `check'
In the expression: check simReset realReset

Why? And how do I fix it?

(This topic started here: Lift to fix the *inside* of a monad transformer stack)


回答1:


In your implementation, check is going to return an IO Bool regardless of what the state monad s is. So, when you pass simReset to check, which is a monadic action in the State Db monad, the return value is going to be State Db (IO Bool).

How to fix it depends on what you're trying to do. Based on your implementation of reset it seems like you're trying to interface with a transformer stack of the form StateT Db IO a. In this case, you're describing a kind of program in the context of StateT Db IO. There are two ways to go about this:

  1. You can upgrade simReset to have type MonadState Db s => s () (This shouldn't actually require any implementation change).

  2. You can define an auxiliary function that "hoists" it into the proper monad

For example:

hoistState :: Monad m => State s a -> StateT s m a
hoistState prg = StateT $ \st -> return $ runState prg st

In either case, you'll probably want to keep the action that you're performing and the result in the same monad:

check :: (Eq a, MonadIO s, MonadState d s) => s a -> IO a -> s Bool

Then, with solution one, you have

reset = check simReset realReset

And with solution two you have:

reset = check (hoistState simReset) realReset      


来源:https://stackoverflow.com/questions/27231320/how-to-combine-two-different-monads

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!