富有表现力和可组合的错误类型

jbe*_*man 18 error-handling haskell composition

我正在努力研究在我正在处理的库中报告错误的一组函数中的错误的最佳方法.

具体来说,我的功能看起来像:

foo, bar, baz :: a -> Maybe a
Run Code Online (Sandbox Code Playgroud)

这里foo只能用一种方法(很适合可能会失败Maybe),但barbaz可以以两种不同的方式失败每个(良好拟合的Either BarErrorsEither BazErrors).

一个解决方案是创建:

data AllTheErrors = TheFooError
                  | BarOutOfBeer
                  | BarBurnedDown
                  | ...
Run Code Online (Sandbox Code Playgroud)

并且使所有的功能恢复Either AllTheErrors,这表示可能通过提高误差范围组成的序列,这些功能在表达误差范围可能每个牺牲个别功能.

有没有办法可以同时获得两者?也许还有除了monadic组合之外的东西?或者与家庭类型(波浪手)...?

Dav*_*ani 16

Control.Monad.Exception库允许强类型的异常在非IO代码中使用.这允许函数抛出错误,并且容易构成抛出不同错误的函数.例如:

{-# 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"
Run Code Online (Sandbox Code Playgroud)

有关使用此库的更多详细信息,另请参阅有关Haskell的明确类型的异常可扩展的动态类型的异常层次结构的文章.