6

如果您查看以下示例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
4

2 回答 2

6

这是作用域类型变量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是实现 typeclass 的不同类型Exception。我们还看到这toException/fromException是一种包装/展开机制,它允许将 typeException的值转换为 types 的值或从 types 的值转换IOExceptionArithException等等。

所以,我们可以这样写:

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确保它以这种方式发生。

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

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

于 2010-02-16T14:07:21.017 回答
1
case () of 
  ()| foo expr1 -> handleFooCase
    | bar expr2 -> handleBarCase
    | otherwise -> blah
于 2010-02-17T01:39:08.490 回答