1

我有一个带有各种构造函数的自定义错误类型,我们称之为MyError

data MyError = ConditionA String | ConditionB String | ConditionC String

构造函数对错误类型进行分类,字符串提供更多详细信息。我想在Eithermonad 中使用我的错误类型,例如我想要一个函数

myFunction :: a -> Either MyError a

在 myFunction 中,我想使用MissingH 中的maybeToEither函数:Data.Either.Utils

maybeToEither :: MonadError e m => e -> Maybe a -> m a

MyError但是ghc告诉我,要做到这一点,我必须制作Error. MonadError这似乎归结为需要是一个单子的事实m,并且单子实例Either e需要Error e因为fail

instance (Error e) => Monad (Either e) where
    return        = Right
    Left  l >>= _ = Left l
    Right r >>= k = k r
    fail msg      = Left (strMsg msg)

那么我怎样才能避免为 做一个无意义的Error实例声明MyError呢?

我注意到Database.MongoDB.Query他们的数据类型的作者有同样的问题Failure(它也有多个构造函数,因此没有合理Error的实例),他们的解决方案是将使用fail视为错误:

instance Error Failure where strMsg = error

这是我最好的选择吗?

4

2 回答 2

3

不要为此使用 MissingH 库。只需使用与 GHC 编译器的每个版本捆绑在一起的基本库。除非您的代码出于某种基本原因明确要求使用MonadError该类(来自 mtl 库),否则您可以避免使用该类,从而避免需要Error错误类型的实例。

从 2010 年 11 月发布的基础库 4.3 版开始,标准Monad实例Either e不需要是. 所以你可以只包括这一行eError

import Control.Monad.Instances ()

Either MyError在模块的顶部,然后随意使用类型Monad

使用此函数而不是 MissingH 的maybeToEither

maybeToEither :: e -> Maybe a -> Either e a
maybeToEither e = maybe (Left e) Right

如果您确实需要 的MonadError实例Either,则必须修改您的MyError类型以以某种人为的方式提供Error实例。有关如何执行此操作的一些更详细的建议,请参阅@jozefg 的答案。即便如此,我个人也不会为了像这样简单的事情而费心拉入整个 MissingH 库。

于 2013-09-30T16:19:04.473 回答
1

你有3个选择

  1. 将案例添加到MyError

    data MyError = FailCase String
                 | ...
    

    很简单,就是有点丑。

  2. 传给error

    这是数据库句柄的 dd ,但运行时错误很糟糕,所以也许应该避免这种情况。

  3. 包裹MyError在一个Either(或Maybe,或其他一些东西来处理错误情况)。

    type MyErrorMonad = Either (Either String MyError)
    

    然后你只需要定义一些同义词。它更多的是打字,但可能是概念上最干净的。它迫使您明确处理fail被调用的情况。

    caseA = Right . CaseA
    ....
    

    instance Error (Either String b) where strMsg = Left
    
于 2013-09-30T14:43:14.927 回答