Haskell 中可以定义自定义防护机制吗?
-
20-09-2019 - |
题
如果你看一下这个例子 catches
:
f = expr `catches` [Handler (\ (ex :: ArithException) -> handleArith ex),
Handler (\ (ex :: IOException) -> handleIO ex)]
看起来像 catches
定义了一个自定义机制来匹配模式(两种异常类型)。我是否弄错了,或者这可以概括为允许定义一个可以采用与特定模式匹配的 lambda 函数的函数吗?
编辑:以下仅供参考,是 GHC 渔获量来源。如果有人可以阐明它是如何工作的,那就太好了。
catches :: IO a -> [Handler a] -> IO a
catches io handlers = io `catch` catchesHandler handlers
catchesHandler :: [Handler a] -> SomeException -> IO a
catchesHandler handlers e = foldr tryHandler (throw e) handlers
where tryHandler (Handler handler) res
= case fromException e of
Just e' -> handler e'
Nothing -> res
解决方案
这是 作用域类型变量 GHC 扩展正在工作。点击链接了解更多信息。
基本上,您定义一个关于类型的断言,模式必须满足该类型才能匹配。所以,是的,它类似于守卫,但又不完全如此。
这个特定的例子是如何工作的?投身于 “基础”库的来源 找出:
class (Show e) => Exception e where
toException :: e -> SomeException
fromException :: SomeException -> Maybe e
data SomeException = forall e . Exception e => SomeException e
instance Exception IOException where
toException = IOException
fromException (IOException e) = Just e
fromException _ = Nothing
instance Exception ArithException where
toException = ArithException
fromException (ArithException e) = Just e
fromException _ = Nothing
我们看到 IOException
和 ArithException
是实现类型类的不同类型 Exception
. 。我们还看到 toException/fromException
是一种包装/展开机制,允许转换类型的值 Exception
到/从类型值 IOException
, ArithException
, , ETC。
所以,我们可以写:
f = expr `catches` [Handler handleArith,
Handler handleIO]
handleArith :: ArithException -> IO ()
handleArith ex = ....
handleIO :: IOException -> IO ()
handleIO ex = ....
假设 IOException
发生。什么时候 catchesHandler
处理处理程序列表的第一个元素,它调用 tryHandler
, ,这称为 fromException
. 。从定义 tryHandler
由此可见,返回类型 fromException
应该与参数相同 handleArith
. 。另一方面, e
属于Exception类型,即-(IOException ...)。因此,类型以这种方式发挥作用(这不是有效的 haskell,但我希望您明白我的意思):
fromException :: (IOException ...) -> Maybe ArithException
来自 instance Exception IOException ...
紧接着结果是 Nothing
, ,因此该处理程序被跳过。通过同样的推理,将调用以下处理程序,因为 fromException
会回来 (Just (IOException ...))
.
所以,您使用了类型签名 handleArith
和 handleIO
指定何时调用它们中的每一个,以及 fromException/toException
确保事情是这样发生的。
如果您愿意,您还可以约束类型 handleIO
和 handleArith
的定义里面 f
, ,使用作用域类型变量。可以说,这可以为您提供更好的可读性。
最终确定作用域类型变量并不是这里的主要参与者。它们只是为了方便而使用。玩这种把戏的主要机制是 fromException/toException
和朋友。作用域类型变量只是允许您拥有更类似于保护模式的语法。
其他提示
case () of
()| foo expr1 -> handleFooCase
| bar expr2 -> handleBarCase
| otherwise -> blah