如果我在这里使用了错误的术语,请提前道歉。
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)
等。
是否有更简单或更惯用的方式来处理这样的场景?
答案 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
当然GError
实例是完全可自定义的。您可以在特定上下文中包含ErrorCatA
和ErrorCatB
的所有行为。
答案 2 :(得分:3)
This论文解释了如何实施Control.Exception。我认为它可以称为可扩展的sum-type(某些任意一族(sub)类型的顶级类型,因此" super-type",而一个简单的sum类型,如{{1} }是不可扩展的,但仍然是Either a b
和a
的超类型,是一个固定的子类型族。 b
库中的Prism
与您有关,请参阅Control.Lens.Prism和Control.Exception.Lens以了解异常情况。
例外情况:
lens
或者您可以编写Exception
- 与SomeException
一样的类 - 就像您自己使用的包装一样(也可以添加棱镜)。