避免声明无意义的错误实例

时间:2013-09-30 14:21:54

标签: haskell error-handling

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

data MyError = ConditionA String | ConditionB String | ConditionC String

构造函数对错误类型进行分类,字符串提供了更多细节。 我想在Either monad中使用我的错误类型,例如我想要一个像

这样的函数
myFunction :: a -> Either MyError a

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

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

但ghc告诉我要做到这一点,我必须制作MyErrorError的实例。这似乎归结为MonadError要求m成为monad的事实,Either e的monad实例因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

这是我最好的选择吗?

2 个答案:

答案 0 :(得分:3)

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

从2010年11月发布的基础库4.3版开始 - Monad的标准Either e实例要求e成为实例Error。所以你可以只包括

import Control.Monad.Instances ()

位于模块顶部,然后随意使用Either MyError类型作为Monad

使用此功能代替MissingH的maybeToEither

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

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

答案 1 :(得分: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