【问题标题】:Can a custom guard mechanism be defined in Haskell?可以在 Haskell 中定义自定义保护机制吗?
【发布时间】:2011-01-17 06:16:13
【问题描述】:

如果您查看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

【问题讨论】:

    标签: haskell design-patterns guard


    【解决方案1】:

    这是工作中的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。根据tryHandler 的定义,fromException 的返回类型应该与handleArith 的参数相同。另一方面,e 属于异常类型,即 - (IOException ...)。所以,类型以这种方式发挥作用(这不是一个有效的 haskell,但我希望你明白我的意思):

    fromException :: (IOException ...) -> Maybe ArithException
    

    instance Exception IOException ... 立即得出结果是Nothing,因此跳过此处理程序。出于同样的原因,将调用以下处理程序,因为fromException 将返回(Just (IOException ...))

    所以,您使用了handleArithhandleIO 的类型签名来指定它们各自的调用时间,并且fromException/toException 确保它以这种方式发生。

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

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

    【讨论】:

    • 作用域类型变量是如何涉及的?我没有看到这种机制在捕获源中起作用。
    【解决方案2】:
    case () of 
      ()| foo expr1 -> handleFooCase
        | bar expr2 -> handleBarCase
        | otherwise -> blah
    

    【讨论】:

      猜你喜欢
      • 2011-07-23
      • 1970-01-01
      • 1970-01-01
      • 2013-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-05
      • 2012-03-04
      相关资源
      最近更新 更多