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

发布于 2024-08-21 05:32:35 字数 795 浏览 12 评论 0原文

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

If you look at the example for catches:

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

It looks like catches has defined a custom mechanism to match on patterns (the two exception types). Am I mistaken, or can this be generalized to allow one to define a function that can take lambda functions that match on a certain pattern?

Edit: FYI below is the GHC source for catches. If someone can shed some light on how this works it would be great.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一生独一 2024-08-28 05:32:35

这是起作用的作用域类型变量 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 类型的值。 。

因此,我们可以这样写:

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 ... 立即得出结果是 什么都没有,所以这个处理程序被跳过。出于同样的原因,将调用以下处理程序,因为 fromException 将返回 (Just (IOException ...))

因此,您使用了 handleArithhandleIO 的类型签名来指定何时调用它们,并且 fromException/toException 确保事情是这样发生的。

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

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

This is the Scoped Type Variables GHC extension at work. Follow the link to learn more.

Basically, you define an assertion about type that have to be met by the patter before it can match. So, yeah, it is akin to guards, but not completely so.

How this particular example works? Dive into sources of "base" library to find out that:

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

We see that IOException and ArithException are different types implementing the typeclass Exception. We also see that toException/fromException is a wrapping/unwrapping mechanism that allows one to convert values of type Exception to/from values of types IOException, ArithException, etc.

So, we could've written:

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

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

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

Suppose that IOException happens. When catchesHandler processes first element of the handlers list, it calls tryHandler, which calls fromException. From the definition of tryHandler it follows that return type of the fromException should be the same as argument of handleArith. On the other hand, e is of type Exception, namely - (IOException ...). So, the types play out this way (this is not a valid haskell, but I hope that you get my point):

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

From the instance Exception IOException ... it follows immediately that the result is Nothing, so this handler is skipped. By the same reasoning the following handler would be called, because fromException would return (Just (IOException ...)).

So, you've used type signatures of handleArith and handleIO to specify when each of them would be called, and fromException/toException made sure that it happened this way.

If you want to, you could also constraint types of handleIO and handleArith inside the definition of f, using scoped type variables. Arguably, this could give you better readability.

Finalizing, Scoped Type Variables are not a primary players here. They are just used for convenience. Main machinery for playing this kind of tricks is fromException/toException and friends. Scoped Type Variables just allow you to have syntax which more closely resemble guard patterns.

离线来电— 2024-08-28 05:32:35
case () of 
  ()| foo expr1 -> handleFooCase
    | bar expr2 -> handleBarCase
    | otherwise -> blah
case () of 
  ()| foo expr1 -> handleFooCase
    | bar expr2 -> handleBarCase
    | otherwise -> blah
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文