如果你看一下这个例子 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

我们看到 IOExceptionArithException 是实现类型类的不同类型 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 ...)).

所以,您使用了类型签名 handleArithhandleIO 指定何时调用它们中的每一个,以及 fromException/toException 确保事情是这样发生的。

如果您愿意,您还可以约束类型 handleIOhandleArith 的定义里面 f, ,使用作用域类型变量。可以说,这可以为您提供更好的可读性。

最终确定作用域类型变量并不是这里的主要参与者。它们只是为了方便而使用。玩这种把戏的主要机制是 fromException/toException 和朋友。作用域类型变量只是允许您拥有更类似于保护模式的语法。

其他提示

case () of 
  ()| foo expr1 -> handleFooCase
    | bar expr2 -> handleBarCase
    | otherwise -> blah
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top