Haskell中的超类型

pas*_*ssy 8 haskell types

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

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

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

data ErrorCatA = WTFError String | OMGError String
data ErrorCatB = BadError Int | TerribleError Float
Run Code Online (Sandbox Code Playgroud)

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

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

这可能吗?

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

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

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

handleError $ wrap $ WTFError "Woopsie"

但我需要改变handleError以匹配CatA (WTFError s)等.

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

bhe*_*ilr 7

假设您有异常类型

data HttpException  -- from http-client package
data MyCustomError = WTFError String | OMGError String
data AnotherError = BadError Int | TerribleError Float
Run Code Online (Sandbox Code Playgroud)

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

data AllErrors = A HttpException | B MyCustomError | C AnotherError
Run Code Online (Sandbox Code Playgroud)

你真正想要的是处理每个例外.那么为什么不这样做呢?写下这些功能

handleHttpError    :: HttpException -> IO ()
handleCustomError  :: MyCustomError -> IO ()
handleAnotherError :: AnotherError  -> IO ()
Run Code Online (Sandbox Code Playgroud)

然后写一堂课

class HandledError e where
    handleError :: e -> IO ()
Run Code Online (Sandbox Code Playgroud)

instance HandledError HttpException where
    handleError = handleHttpError

instance HandledError MyCustomError where
    handleError = handleCustomError

instance HandledError AnotherError where
    handleError = handleAnotherError
Run Code Online (Sandbox Code Playgroud)

只需handleError在需要的地方使用.它与你所拥有的并没有什么不同,但现在处理一种错误的逻辑并没有与处理另一种错误的逻辑混合在一起.只要想到班级handleError和你的班级一样handleError . wrap.


Sho*_*hoe 4

我会创建一个类型类:

class GError a where
    errorMessage :: a -> String
Run Code Online (Sandbox Code Playgroud)

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

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

并像这样使用它:

handleError :: GError a => a -> IO ()
handleError = putStrLn . errorMessage
Run Code Online (Sandbox Code Playgroud)

Live demo

当然,GError实例是完全可定制的。ErrorCatA您可以包含特定上下文中的任何行为ErrorCatB