haskell - 如何组合两个不同的 monad

标签 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)

最佳答案

在您的实现中,check 将返回一个 IO Bool,无论状态 monad s 是什么。因此,当您传递 simReset 进行检查(这是 State Db monad 中的一个单子(monad)操作)时,返回值将是 State Db (IO Bool) .

如何修复它取决于您想要做什么。根据您的 reset 实现,您似乎正在尝试与 StateT Db IO a 形式的变压器堆栈进行交互。在本例中,您将在 StateT Db IO 上下文中描述一种程序。有两种方法可以解决这个问题:

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

  2. 您可以定义一个辅助函数,将其“提升”到适当的 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/

相关文章:

haskell - 结合两个枚举

javascript - 使用 Prism 输出语义代码块

Haskell 99 个问题 #8 : Can't understand foldr

haskell - `Except` 的复杂性在 Haskell 中有何用途?

haskell - 如何使用Monad变形金刚来组合不同的(纯净的和不纯净的)monad?

haskell - 如何使用自定义App类型代替IO?

haskell "Could not deduce..."

haskell - 如何使用 map 和过滤器实现列表理解?

unit-testing - 是否可以使用类型类将 `ReaderT (IO a) IO a` 更改为 `ReaderT (i a) IO a` ?

haskell - 将 monad 添加到变压器堆栈的中间