haskell - 失败时如何保存信息?

标签 haskell monads monad-transformers state-monad

我正在编写一些代码,使用 StateT monad 转换器来跟踪一些状态信息(日志记录等)。

我传递给 StateT 的 monad 非常简单:

data CheckerError a = Bad {errorMessage :: Log} | Good a
    deriving (Eq, Show)


instance Monad CheckerError where
    return x = Good x

    fail msg = Bad msg

    (Bad msg) >>= f = Bad msg
    (Good x) >>= f = f x

type CheckerMonad a = StateT CheckerState CheckerError a

这只是一个变体。

令我困扰的是失败的定义。在我的计算中,我在这个单子(monad)中产生了大量信息,即使失败,我也想保留这些信息。 目前我唯一能做的就是将所有内容转换为 String 并创建一个 Bad 实例,并将 String 作为参数传递给 失败

我想做的是:

fail msg = do
    info <- getInfoOutOfTheComputation
    return $ Bad info

但是到目前为止我尝试的所有内容都会出现类型错误,可能是因为这会混合不同的单子(monad)。

无论如何,我是否可以实现fail以保留我需要的信息,而不必将其全部转换为String

我不敢相信 Haskell 能实现的最好的结果是使用 show+read 将所有信息作为字符串传递给 fail

最佳答案

您的 CheckerError monad 与 Either monad 非常相似。我将在我的答案中使用 Either monad (及其对应的 monad 转换器 ErrorT )。

单子(monad)变形器有一个微妙之处:顺序很重要。 “内部”单子(monad)中的影响比“外部”层引起的影响具有首要性。考虑 CheckerMonad 的这两个替代定义:

import Control.Monad.State
import Control.Monad.Error

type CheckerState = Int     -- dummy definitions for convenience
type CheckerError = String

type CheckerMonad a = StateT CheckerState (Either String) a

type CheckerMonad' a = ErrorT String (State CheckerState) a

CheckerMonad中,Either是内部monad,这意味着失败将删除整个状态。注意这个 run 函数的类型:

runCM :: CheckerMonad a -> CheckerState -> Either CheckerError (a,CheckerState)
runCM m s = runStateT m s

您要么失败,要么返回结果以及截至该点的状态。

CheckerMonad'中,State是内部monad。这意味着即使发生故障,状态也会被保留:

runCM' :: CheckerMonad' a -> CheckerState -> (Either CheckerError a,CheckerState)
runCM' m s = runState (runErrorT m) s

返回一对,其中包含截至该点的状态以及失败或结果。

需要一些练习才能培养出如何正确排序 monad 转换器的直觉。图表中Type juggling this Wikibook page 部分是一个很好的起点。

此外,最好避免使用 fail直接,因为它被认为是语言中的一个缺点。相反,请使用错误转换器提供的专门函数来抛出错误。当使用 ErrorTMonadError 的一些其他实例时,使用throwError .

sillycomp :: CheckerMonad' Bool
sillycomp = do
    modify (+1)
    s <- get 
    if s == 3
        then throwError "boo"
        else return True

*Main> runCM' sillycomp 2
Loading package transformers-0.3.0.0 ... linking ... done.
Loading package mtl-2.1.2 ... linking ... done.
(Left "boo",3)

*Main> runCM' sillycomp 3
(Right True,4)

ErrorT 有时使用起来很烦人,因为与 Either 不同,它需要 Error对错误类型的约束。 Error 类型类强制您定义两个错误构造函数 noMsgstrMsg,这对于您的类型可能有意义,也可能没有意义。

您可以使用EitherT来自either包,它允许您使用任何类型作为错误。使用 EitherT 时,使用 left 函数抛出错误。

关于haskell - 失败时如何保存信息?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23127973/

相关文章:

haskell - 如果两个 monad 转换器属于不同类型,但它们的底层 monad 属于同一类型,是否有原则性的方法来组合它们?

haskell - 如何将可变向量放入状态单子(monad)

haskell - "try"回溯了多远?

windows - Windows 上 GHC 的 64 位基础库的动态版本

haskell - 如何读取上传文件的内容

scala - TailRec 和 State monad 的组成

haskell - 是否可以在单子(monad)序列中更改单子(monad)类型?

scala - 如何向上转换 monad 变压器类型?

haskell - 获取类型参数的标识?

javascript - 将Fluture与Ramda一起使用