如何结合两个不同的monad

时间:2014-12-01 15:09:22

标签: haskell monad-transformers

我正在测试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

1 个答案:

答案 0 :(得分:2)

在您的实施中,无论状态monad check是什么,IO Bool都将返回s。因此,当您通过simReset检查State Db monad中的monadic操作时,返回值将为State Db (IO Bool)

如何解决这个问题取决于您尝试做什么。根据您对reset的实现,您似乎正在尝试与StateT Db IO a形式的转换器堆栈进行交互。在这种情况下,您在StateT Db IO的上下文中描述了一种程序。有两种方法可以解决这个问题:

  1. 您可以将simReset升级为MonadState Db s => s ()类型(这实际上不需要进行任何实施更改)。

  2. 您可以定义一个"提升"它进入了正确的monad

  3. 例如:

    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