Haskell中的超类型

时间:2014-05-14 17:57:06

标签: haskell types

如果我在这里使用了错误的术语,请提前道歉。

Haskell中用于概括两种或更多类型的惯用方法是什么,以便您可以在避免样板代码的同时推迟对它们进行模式匹配?

举一个具体的例子:在我的应用程序中,我想传递执行期间可能发生的错误。这些错误来自不同的模块,所以我不直接控制它们:

data ErrorCatA = WTFError String | OMGError String
data ErrorCatB = BadError Int | TerribleError Float

现在我想传递这些错误类别的某种形式的超类型,所以我可以像这样处理它们:

handleError :: GenericError -> IO ()
handleError err =
    putStrLn $ case err of
        WTFError s -> "WTF?! " ++ s
        OMGError s -> "OMG?! " ++ s
        BadError i -> if i > 5 then "Really bad" else "Not that bad"
        TerribleError f -> "Terrible! " ++ show f

这可能吗?

我通过创建这样的包装器类型得到了最近的:

data GenericError = CatA ErrorCatA | CatB ErrorCatB
class GError a where
    wrap :: a -> GenericError

instance GError ErrorCatA where
    wrap = CatA

instance GError ErrorCatB where
    wrap = CatB

通过这样做,我可以轻松地包装所有错误,如

handleError $ wrap $ WTFError "Woopsie"

但我需要更改handleError以匹配CatA (WTFError s)等。

是否有更简单或更惯用的方式来处理这样的场景?

3 个答案:

答案 0 :(得分:7)

假设您有异常类型

data HttpException  -- from http-client package
data MyCustomError = WTFError String | OMGError String
data AnotherError = BadError Int | TerribleError Float

你想要单独处理每一个,但一般来说。而不是在它们周围写一个和类型

data AllErrors = A HttpException | B MyCustomError | C AnotherError

你真正想要的是处理每个例外。那么为什么不这样做呢?写下函数

handleHttpError    :: HttpException -> IO ()
handleCustomError  :: MyCustomError -> IO ()
handleAnotherError :: AnotherError  -> IO ()

然后写一个类

class HandledError e where
    handleError :: e -> IO ()

使用

instance HandledError HttpException where
    handleError = handleHttpError

instance HandledError MyCustomError where
    handleError = handleCustomError

instance HandledError AnotherError where
    handleError = handleAnotherError

只需在需要的地方使用handleError即可。它与你所拥有的并没有什么不同,但现在处理一种错误的逻辑并没有与处理另一种错误的逻辑混合在一起。只需将班级handleError视为与handleError . wrap相同。

答案 1 :(得分:4)

我要创建一个类型类:

class GError a where
    errorMessage :: a -> String

并向其提供有意义的实例:

instance GError ErrorCatA where
    errorMessage (WTFError s) = "WTF?! " ++ s
    errorMessage (OMGError s) = "OMG?! " ++ s

instance GError ErrorCatB where
    errorMessage (BadError i)      = "Bad! " ++ show i
    errorMessage (TerribleError f) =  "Terrible! " ++ show f

并使用它:

handleError :: GError a => a -> IO ()
handleError = putStrLn . errorMessage

Live demo

当然GError实例是完全可自定义的。您可以在特定上下文中包含ErrorCatAErrorCatB的所有行为。

答案 2 :(得分:3)

This论文解释了如何实施Control.Exception。我认为它可以称为可扩展的sum-type(某些任意一族(sub)类型的顶级类型,因此" super-type",而一个简单的sum类型,如{{1} }是不可扩展的,但仍然是Either a ba的超类型,是一个固定的子类型族。 b库中的Prism与您有关,请参阅Control.Lens.PrismControl.Exception.Lens以了解异常情况。


例外情况:

lens

或者您可以编写Exception - 与SomeException一样的类 - 就像您自己使用的包装一样(也可以添加棱镜)。