如何在失败时保存信息? [英] How to preserve information when failing?

查看:197
本文介绍了如何在失败时保存信息?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



传递给 StateT 的monad非常简单:

  data CheckerError a = Bad {errorMessage :: Log} |好的
派生(Eq,Show)


实例Monad CheckerError其中
返回x =好x

失败msg =错误消息

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

类型CheckerMonad a = StateT CheckerState CheckerError a

它只是一个 Left Right variant。



麻烦的是失败。在我的计算中,我在这个monad中产生了很多信息,即使失败,我也想保留这些信息。
目前我唯一可以做的就是将所有内容转换为 String 并创建一个 Bad 实例字符串作为参数传递给失败



什么我想要做的就是这样:

  fail msg = do 
info< - getInfoOutOfTheComputation
返回$ Bad info

然而,我现在试过的所有东西都给出了类型错误,可能是因为这会混合不同monads。



无论如何,我可以在其中执行失败,以便保留我需要的信息而不必将它全部转换为 String



我不相信Haskell能够实现的最好方法是使用 show + read 将所有信息作为字符串传递给 fail CheckerError monad与<$ c $非常相似。 C>要么 monad。我将使用或者 monad(和它的monad转换器对应 ErrorT )。

monad trasformers有一个微妙之处:订单很重要。 内部monad中的效果优先于外部层引起的效果。考虑这两个 CheckerMonad 的替代定义:

  import Control.Monad。状态
导入Control.Monad.Error

类型CheckerState = Int - 虚拟定义为了方便
类型CheckerError =字符串

类型CheckerMonad a = StateT CheckerState(任一字符串)a

类型CheckerMonad'a = ErrorT字符串(State CheckerState)a

CheckerMonad 中,或者是内部monad,这意味着失败会擦除整个状态。注意这个运行函数的类型:

  runCM :: CheckerMonad a  - > CheckerState  - > CheckerError(a,CheckerState)
runCM ms = runStateT ms

您要么失败,要么

CheckerMonad'中返回一个结果,州是内部monad。这意味着即使发生故障,状态也会被保留:

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

返回一对,其中包含到目前为止的状态,以及失败或结果。



需要一些练习来发展一种直觉,正确订购monad变压器。 类型杂耍部分的图表en.wikibooks.org/wiki/Haskell/Monad_transformersrel =nofollow>这个Wikibook页面是一个很好的起点。

另外,最好避免使用 fail ,因为它在语言中被认为有点瑕疵。相反,使用专用函数来抛出错误转换器提供的错误。当使用 ErrorT 或其他一些 MonadError ,请使用 throwError

 sillycomp :: CheckerMonad'Bool 
sillycomp = do
修改(+1)
s< - 获得
如果s == 3
then throwErrorboo
else返回True

* Main> runCM'sillycomp 2
加载软件包变形金刚 - 0.3.0.0 ...链接...完成。
加载包mtl-2.1.2 ...链接...完成。
(Leftboo,3)

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

ErrorT 有时很烦人,因为与 不同,它需要一个 Error 限制错误类型。 Error typeclass强制你定义两个错误构造函数 noMsg strMsg

-4.1.1 / docs / Control-Monad-Trans-Either.htmlrel =nofollow> EitherT 来自 或者 包,它可以让你使用任何类型的错误。使用 EitherT ,使用 left 函数抛出错误。


I'm writing some code that uses the StateT monad transformer to keep track of some stateful information (logging and more).

The monad I'm passing to StateT is very simple:

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

It's just a Left and Right variant.

What troubles me is the definition of fail. In my computation I produce a lot of information inside this monad and I'd like to keep this information even when failing. Currently the only thing I can do is to convert everything to a String and create a Bad instance with the String passed as argument to fail.

What I'd like to do is something like:

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

However everything I tried until now gives type errors, probably because this would mix different monads.

Is there anyway in which I can implement fail in order to preserve the information I need without having to convert all of it into a String?

I cannot believe that the best Haskell can achieve is using show+read to pass all the information as the string to fail.

解决方案

Your CheckerError monad is very similar to the Either monad. I will use the Either monad (and its monad transformer counterpart ErrorT) in my answer.

There is a subtlety with monad trasformers: order matters. Effects in the "inner" monad have primacy over effects caused by the "outer" layers. Consider these two alternative definitions of 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

In CheckerMonad, Either is the inner monad, and this means a failure will wipe the whole state. Notice the type of this run function:

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

You either fail, or return a result along with the state up to that point.

In CheckerMonad', State is the inner monad. This means the state will be preserved even in case of failures:

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

A pair is returned, which contains the state up to that point, and either a failure or a result.

It takes a bit of practice to develop an intuition of how to properly order monad transformers. The chart in the Type juggling section of this Wikibook page is a good starting point.

Also, it is better to avoid using fail directly, because it is considered a bit of a wart in the language. Instead, use the specialized functions for throwing errors provided by the error transformer. When working with ErrorT or some other instance of MonadError, use 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 is sometimes annoying to use because, unlike Either, it requires an Error constraint on the error type. The Error typeclass forces you to define two error constructors noMsg and strMsg, which may or may not make sense for your type.

You can use EitherT from the either package instead, which lets you use any type whatsoever as the error. When working with EitherT, use the left function to throw errors.

这篇关于如何在失败时保存信息?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆