我正在测试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)
答案 0 :(得分:2)
在您的实施中,无论状态monad check
是什么,IO Bool
都将返回s
。因此,当您通过simReset
检查State Db
monad中的monadic操作时,返回值将为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
使用解决方案2,你有:
reset = check (hoistState simReset) realReset