gpt4 book ai didi

haskell - 如何将两个 ErrorT monad 转换器堆叠在一起?

转载 作者:行者123 更新时间:2023-12-02 05:06:14 26 4
gpt4 key购买 nike

假设我有这两个功能:

errorm :: ( MonadError String m ) => Bool ->  m Int
errorm cond = if cond then return 1 else throwError "this is an error"

errorms :: ( MonadError String m ) => Bool -> m String
errorms cond = if cond then return "works" else throwError "does not work"

如你所见,一个在安全情况下返回一个字符串,而另一个返回一个int

我现在想在另一个 monad 中一起使用它们。平凡地:

errErr :: MonadError String m => Bool -> Bool -> m (Int, String)
errErr b1 b2 = do
a <- errorm b1
b <- errorms b2
return (a,b)

这里的函数签名是GHC派生的,不知道怎么用这个函数。我试过这个:

runErrorT ( runErrorT ( errErr1 True True ) )  -- should give me (Right 1, Right "works")

但它却给了我:

Ambiguous type variable `e0' in the constraint:
(Error e0) arising from a use of `errErr1'
Probable fix: add a type signature that fixes these type variable(s)
In the first argument of `runErrorT', namely `(errErr1 True True)'
In the first argument of `runErrorT', namely
`(runErrorT (errErr1 True True))'
In the expression: runErrorT (runErrorT (errErr1 True True))

一般来说,这只是我遇到的问题之一。我觉得我没有掌握如何准确地堆叠两个属于同一类但具有不同类型参数的 monadT。另一个例子可能是堆叠一对函数:

f :: ( MonadState Int m ) => m ()
g :: ( MonadState String m ) => m ()

-------------------------------------------- - - - 更新 - - - - - - - - - - - - - - - - - - - - - - --------

根据下面 Daniel 的评论,我从上面添加了函数 f 和 g 的具体实例。但是多亏了 Tikhon 的回答,我想我明白了。

type Counter = Int
type Msg = String

incr :: (MonadState Counter m) => Counter -> m ()
incr i = modify (+i)

addMsg :: ( MonadState Msg m ) => Msg -> m()
addMsg msg = modify ( ++ msg )

incrMsg:: (MonadTrans t, MonadState Msg m, MonadState Counter (t m)) => t m ()
incrMsg = do
lift . addMsg $ "one"
incr 1
return ()

incrMsgt = runIdentity $ runStateT ( runStateT incrMsg 1 ) "hello" :: (((), Int), String)

最佳答案

在这种特殊情况下,您不需要需要堆叠两个转换器——因为它们都是MonadError String,它们可以相同使用em> 单子(monad)。您可以将 errormerrorms 一起使用,就像在任何其他 monad 中使用两个值一样。

作为更具体的解释,暂时忽略转换器:您可以想象这些值只是Either String IntEither String String。显然,您可以将它们一起使用。这就是为什么最后只需要一个 runErrorT 而不是两个:两个值都在同一个 monad 中。

现在,您的实际问题是什么:如何堆叠两个 monad 转换器?它的工作方式就像组合任意两个 monad 转换器。两个堆叠在一起的状态转换器看起来就像两个不同的转换器堆叠在一起。

现在,使用它们有点棘手。根据您使用的是哪个,您将需要以不同方式使用 lift。如果你在基础 monad 中有一个值,你需要提升两次。如果你在内部状态 monad 中有一个值,你将需要使用它一次。如果你在外层有一个,你根本不需要它。这就像普通的变压器。

回到您的错误示例,假设您实际上确实想要堆叠两个不同的错误 monad 转换器,而不是将它们作为一个使用。这意味着如果您想在 inner 中抛出错误,则必须编写 lift (throwError "message")。如果您真的这样做了并且有两个堆叠的错误转换器,那么使用 runErrorT 两次就可以了。

关于haskell - 如何将两个 ErrorT monad 转换器堆叠在一起?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16267168/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com