我有一个带有各种构造函数的自定义错误类型,我们称之为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告诉我要做到这一点,我必须制作MyError
和Error
的实例。这似乎归结为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
这是我最好的选择吗?
答案 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个选择
将案例添加到MyError
data MyError = FailCase String
| ...
这很简单,有点难看。
将其传递给error
这就是数据库处理的dd,但运行时错误很糟糕,所以应该避免这种情况。
将MyError
包裹在Either
(或Maybe
或其他一些内容中以处理错误案例。)
type MyErrorMonad = Either (Either String MyError)
然后你只需要定义一些同义词。它打字更多,但可能是概念上最干净的。它会强制您明确处理调用fail
的情况。
caseA = Right . CaseA
....
和
instance Error (Either String b) where strMsg = Left