Should this Perl 6 CATCH block be able to change variables in the lexical scope?

白昼怎懂夜的黑 提交于 2019-12-23 16:26:37

问题


I'm playing with resumable exceptions. In this example, I try to numify something that doesn't numify. I catch that and attempt to give the $value variable an appropirate value then resume execution:

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.";

The CATCH block can see the lexical scope it is in, a resuming picks up where it left off. I expected that I'd be able to change $value and have the rest of the block use that value, but outside of the CATCH the value becomes a Failure:

「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.

What's up?


回答1:


Inside of a try block, use fatal takes effect, to cause lazy exceptions returned from method or sub calls to throw immediately. Outside the lexical scope of a try block, note that:

my $value = +$m;

Would result in a Failure being assigned to $value. The try turns it into something more like:

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

Which you could imagine being defined as something like:

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

(I'm hand-waving because the compiler spits out the code to do this inline and with a few optimizations).

In the case under consideration, the .sink triggers the exception to the thrown. The CATCH block runs. The .resume indicates that we do not wish to unwind the call stack as would normally happen with a CATCH block, so execution continues inside of force-failure, which then returns f - the Failure. This all happens prior to the assignment in the mainline code to $value; the Failure is therefore assigned, overwriting the value given by the CATCH block.

Unfortunately, you can't escape this with //= because that does the test before running the RHS (which is what we usually want it to do). However, it is possible to do:

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

Of course, this is all a bit of a contrived example, since the normal idiom would be to not have a try block at all, and to write it as:

my $value = +$m // 0;

Thus taking advantage of the Failure. In general, resumable exceptions need a good amount of care, because in many cases code will not be written expecting a resumption to take place. It turns out that the code generated for fatalizing a Failure is one such piece.



来源:https://stackoverflow.com/questions/43289842/should-this-perl-6-catch-block-be-able-to-change-variables-in-the-lexical-scope

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!