区分 CATCH 块中的异常和失败 [RAKU]

Distinguish between Exception and Failure in a CATCH block [ RAKU ]

我们知道失败可以由 CATCH 块处理。

在下面的示例中,我们创建了一个 'AdHoc' 失败(在 other-sub 中),我们在 CATCH 块中处理异常(在 my-sub 中)

sub my-sub {
    try {
        CATCH {
            when X::AdHoc { say 'AdHoc Exception handled here'; .resume }
            default {say 'Other Exception'; .resume}
        }

        my $b = other-sub();

        $b.so ?? $b.say !! 'This was a Failure'.say;
    }
}

sub other-sub { fail 'Failure_X' }

my-sub();

输出如下:

AdHoc Exception handled here
This was a Failure

不过我的问题是:我们如何区分失败和 CATCH 块中的 "normal" 异常,以便区分这两种情况?

FailureException之间的关系是一个Failure有一个Exception——也就是说,它持有异常对象作为其状态的一部分.像这样:

class Failure {
    has Exception $.exception;
    # ...
}

Failure "explodes" 时,它通过抛出其中的 Exception 来实现。因此,到达 CATCH 块的是 Exception 对象,并且没有 link 返回封闭的 Failure。 (事实上​​ ,一个给定的 Exception 对象原则上可以由许多 Failure 持有。)

因此,没有直接的方法可以检测到这一点。从设计的角度来看,您可能不应该,并且应该找到一种不同的方法来解决您的问题。 Failure 只是一种推迟抛出异常并允许将其视为值的方法;这并不意味着潜在问题的性质会发生变化,因为它是作为一个值而不是作为控制流的立即转移来传达的。不幸的是,问题中没有说明最初的目标;您可能会发现查看控制异常很有用,但除此之外可能 post 另一个关于您试图解决的潜在问题的问题。可能有更好的方法。

为了完整起见,我会注意到 种间接方法可以检测到 Exception 是由 Failure 抛出的。比如获取异常对象的.backtrace,查看top frame的包,可以判断出来自Failure:

sub foo() { fail X::AdHoc.new(message => "foo") }
try {
    foo();
    CATCH {
        note do { no fatal; .backtrace[0].code.package ~~ Failure };
        .resume
    }
}

但是,这在很大程度上取决于可以轻松更改的实现细节,因此我不会依赖它。

只需删除 try 包装器:

sub my-sub {

#    try {              <--- remove this line...

        CATCH {
            when X::AdHoc { say 'AdHoc Exception handled here'; .resume }
            default {say 'Other Exception'; .resume}
        }

        my $b = other-sub();

        $b.so ?? $b.say !! 'This was a Failure'.say;

#    }                  <--- ...and this one

}

sub other-sub { fail 'Failure_X' }

my-sub();

您使用了 trytry 做了一些事情,但这里相关的事情是它告诉 Raku 立即将其范围内的任何 Failure 提升为异常——这就是你所说的 don 't想要。所以最简单的解决办法就是停止这样做。


这个答案只是冗长地重复了 jnthn 的部分解释(具体参见他在答案下方写的评论)。但我不相信所有读者都会 spot/understand 这方面的,并且认为对 jnthn 的回答的一两条评论不会有帮助,因此这个答案。

我已将此作为社区答案编写,以确保我不会从任何投票中受益,因为它显然不能保证这一点。如果它获得足够多的反对票,我们就会将其删除。