【问题标题】:Should this Perl 6 CATCH block be able to change variables in the lexical scope?这个 Perl 6 CATCH 块是否应该能够更改词法范围内的变量?
【发布时间】:2017-04-08 03:28:50
【问题描述】:

我正在玩可恢复的异常。在这个例子中,我尝试使一些不会 numify 的东西 numify。我抓住了这一点,并尝试给 $value 变量一个适当的值,然后继续执行:

try {
    my $m = 'Hello';
    my $value;
    $value = +$m;
    put "Outside value is 「{$value.^name}」";
    CATCH {
        when X::Str::Numeric {
            put "「$m」 isn't a number!";
            put "Inside value is 「{$value.^name}」";
            $value = 0;
            put "Inside value is now 「$value.」";
            .resume;
            }
        default {
            put "Unhandled type 「{.^name}」";
            }
        }
    put "End of the block";
    }

put "Got to the end.";

CATCH 块可以看到它所在的词法范围,恢复从它停止的地方开始。我希望我能够更改 $value 并让块的其余部分使用该值,但在 CATCH 之外,该值将变为失败:

「Hello」 isn't a number!
Inside value is 「Any」
Inside value is now 「0.」
Outside value is 「Failure」
End of the block
Got to the end.

怎么了?

【问题讨论】:

  • 我怀疑这是评估效果的顺序:异常似乎中断了$value = +$m 语句,分配仅在CATCH 块执行后发生,覆盖0

标签: exception raku


【解决方案1】:

try 块内,use fatal 生效,导致方法或子调用返回的惰性异常立即抛出。在 try 块的词法范围之外,请注意:

my $value = +$m;

会导致将Failure 分配给$valuetry 把它变成了类似的东西:

my $value = force-failure(+$m);

你可以想象被定义为:

sub force-failure(Mu \f) { f.sink if f ~~ Failure; f }

(我在挥手,因为编译器会吐出代码来内联执行此操作并进行一些优化)。

在正在考虑的情况下,.sink 触发异常以抛出。 CATCH 块运行。 .resume 表示我们不希望像CATCH 块通常发生的那样展开调用堆栈,因此在force-failure 内部继续执行,然后返回f - Failure。这一切都发生在主线代码中分配给$value之前;因此分配了Failure,覆盖了CATCH 块给出的值。

不幸的是,您无法使用//= 来避免这种情况,因为它会在运行 RHS 之前进行测试(这是我们通常希望它执行的操作)。但是,可以这样做:

my $numified = +$m;
my $value //= $numified;

当然,这只是一个人为的例子,因为正常的习惯用法是根本没有try 块,并将其写成:

my $value = +$m // 0;

因此利用Failure。一般来说,可恢复的异常需要非常小心,因为在许多情况下,编写代码不会期望恢复发生。事实证明,为使 Failure 致命而生成的代码就是这样的一部分。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2015-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-13
  • 1970-01-01
  • 2011-02-07
  • 1970-01-01
相关资源
最近更新 更多