可表达和可组合的错误类型 [英] Expressive and composable error types
问题描述
在我正在研究的库中,最好的方法是报告一组应该很好组合的函数中的错误,我一直在努力.
I am struggling with the best way to report errors in a set of functions that should compose nicely, in a library I'm working on.
具体来说,我具有如下功能:
Concretely, I have functions that look like:
foo, bar, baz :: a -> Maybe a
其中 foo
只能以一种方式失败(非常适合 Maybe
),但是 bar
和 baz
可能会以两种不同的方式失败(非常适合 Ether BarErrors
和 Ether BazErrors
).
where foo
can fail in only one way (a good fit for Maybe
), but bar
and baz
can fail in two different ways each (good fits for Either BarErrors
and Either BazErrors
).
一种解决方案是创建:
data AllTheErrors = TheFooError
| BarOutOfBeer
| BarBurnedDown
| ...
并使所有函数返回 AllTheErrors
,它表示这些函数的组成序列可能引起的错误范围,但以表示范围为代价每个单个函数可能出现的错误.
and make all the functions return Either AllTheErrors
, which expresses the range of errors that might be raised by a composed sequence of these functions at the expense of expressing the range of errors possible for each individual function.
有没有办法我能兼得?也许除了莫纳德成分之外还有其他东西吗?还是带有类型家族(挥手)...?
Is there a way I can get both? Maybe with something other than monadic composition? Or with type families (waves hands)...?
推荐答案
Control.Monad.Exception 库允许在非IO代码中使用强类型异常.这允许函数引发错误,并轻松地与引发不同错误的函数组合.例如:
The Control.Monad.Exception library allows strongly typed exceptions to be used in non IO code. This allows functions to throw errors, and easily compose with functions that throw different errors. For example:
{-# LANGUAGE RankNTypes, MultiParamTypeClasses, FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
import Prelude hiding (catch)
import Control.Monad.Exception
data FooException = FooException deriving (Show, Typeable)
instance Exception FooException
data BarErrors = BarErrors deriving (Show, Typeable)
instance Exception BarErrors
data BazErrors = BazErrors deriving (Show, Typeable)
instance Exception BazErrors
-- sample functions
foo :: (Throws FooException l) => a -> EM l a
foo a = return a
bar :: (Throws BarErrors l) => a -> EM l a
bar _ = throw BarErrors
baz :: (Throws BazErrors l) => a -> EM l a
baz a = return a
-- using all at once:
allAtOnce :: (Throws FooException l, Throws BarErrors l, Throws BazErrors l) =>
a -> EM l String
allAtOnce x = do
_ <- foo x
_ <- bar x
_ <- baz x
return "success!"
-- now running the code, catching the exceptions:
run :: a -> String
run x = runEM $ allAtOnce x `catch` (\(_ :: FooException) -> return "foo failed")
`catch` (\BarErrors -> return "bar failed")
`catch` (\BazErrors -> return "baz failed")
-- run 3 results in "bar failed"
另请参见论文 Haskell的明确类型异常和
See also the papers Explicitly Typed Exceptions for Haskell and An Extensible Dynamically-Typed Hierarchy of Exceptions for more details on using this library.
这篇关于可表达和可组合的错误类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!