可以在Haskell中定义自定义防护机制吗?

时间:2010-02-16 04:43:19

标签: haskell design-patterns guard

如果你看一下catches的例子:

 f = expr `catches` [Handler (\ (ex :: ArithException) -> handleArith ex),
                     Handler (\ (ex :: IOException)    -> handleIO    ex)]

看起来catches定义了一种自定义机制来匹配模式(两种异常类型)。我错了,或者这可以推广到允许我们定义一个函数,该函数可以采用匹配特定模式的lambda函数?

编辑:下面的FYI是渔获量的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

2 个答案:

答案 0 :(得分:5)

这是Scoped Type Variables GHC扩展工作。点击链接了解更多信息。

基本上,你定义一个关于类型的断言,它必须在模式匹配之前满足。所以,是的,它类似于警卫,但并非完全如此。

这个特定的例子如何运作?深入了解sources of "base" library以了解:

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的值转换为IOExceptionArithException等类型的值。

所以,我们可以写:

f = expr `catches` [Handler handleArith,
                    Handler handleIO]

handleArith :: ArithException -> IO ()
handleArith ex = ....

handleIO :: IOException -> IO ()
handleIO ex = ....

假设IOException发生了。当catchesHandler处理处理程序列表的第一个元素时,它会调用tryHandler,调用fromException。根据{{​​1}}的定义,tryHandler的返回类型应与fromException的参数相同。另一方面,handleArith的类型为Exception,即 - (IOException ...)。所以,类型以这种方式播放(这不是一个有效的haskell,但我希望你明白我的观点):

e

fromException :: (IOException ...) -> Maybe ArithException 开始紧接着结果为instance Exception IOException ...,因此跳过此处理程序。通过相同的推理,将调用以下处理程序,因为Nothing将返回fromException

因此,您已使用(Just (IOException ...))handleArith的类型签名来指定何时调用它们,handleIO确保以这种方式发生。

如果您愿意,还可以使用范围类型变量约束fromException/toException定义中handleIOhandleArith的类型。可以说,这可以为您提供更好的可读性。

最终确定,Scoped Type Variables不是这里的主要参与者。它们仅用于方便。玩这种技巧的主要机器是f和朋友。 Scoped Type Variables只允许您使用更接近保护模式的语法。

答案 1 :(得分:1)

case () of 
  ()| foo expr1 -> handleFooCase
    | bar expr2 -> handleBarCase
    | otherwise -> blah