问题
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