我正在测试 REST 服务器。我在 IO monad 中击中它并在 State Db 中模拟它,其中 Db 跟踪服务器的假定状态。以下函数应该运行两个版本并比较结果...
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
但是当我尝试使用这些最简单的功能时......
simReset :: State Db ()
realReset :: IO ()
reset :: StateT Db IO Bool
reset = check simReset realReset
我收到此错误:
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
为什么?我该如何解决它?
(本主题从这里开始:Lift to fix the *inside* of a monad transformer stack)
最佳答案
在您的实现中,check
将返回一个 IO Bool
,无论状态 monad s
是什么。因此,当您传递 simReset
进行检查(这是 State Db
monad 中的一个单子(monad)操作)时,返回值将是 State Db (IO Bool)
.
如何修复它取决于您想要做什么。根据您的 reset
实现,您似乎正在尝试与 StateT Db IO a
形式的变压器堆栈进行交互。在本例中,您将在 StateT Db IO
上下文中描述一种程序。有两种方法可以解决这个问题:
您可以将
simReset
升级为MonadState Db s => s ()
类型(这实际上不需要任何实现更改)。您可以定义一个辅助函数,将其“提升”到适当的 monad
例如:
hoistState :: Monad m => State s a -> StateT s m a
hoistState prg = StateT $ \st -> return $ runState prg st
无论哪种情况,您可能都希望将正在执行的操作和结果保留在同一个 monad 中:
check :: (Eq a, MonadIO s, MonadState d s) => s a -> IO a -> s Bool
然后,通过解决方案一,您可以得到
reset = check simReset realReset
通过解决方案二,您可以:
reset = check (hoistState simReset) realReset
关于haskell - 如何组合两个不同的 monad,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27231320/