Undocumented escape hatch
On my quest to a custom when-statement I did quite a bit of reading. The study of roast and Actions.nqp can lead to great gain in knowledge.
$ less -N S04-statements/given.t
136 # given returns the correct value:
137 {
138 sub ret_test($arg) {
139 given $arg {
140 when "a" { "A" }
141 when "b" { "B" }
142 }
143 }
144
145 is( ret_test("a"), "A", "given returns the correct value (1)" );
146 is( ret_test("b"), "B", "given returns the correct value (2)" );
147 }
As we can see in this example, the spec asks given
to return the value provided to succeed
. This is an ENODOC. We don’t have to depend on sink to turn the succeed value into a Routine
s return value.
my $result = do given 'a' {
CONTROL { default { say 'seen ', .^name } }
when Str { say 'Str'; succeed('It was a string.'); }
}
dd $result;
# OUTPUT: Str
Str $result = "It was a string."
It’s a bit odd that we need the do
thought, as given
will always return at least Nil
. The oddity doesn’t stop there. We can get hold of control exceptions. Some of which can return a value. That value is well hidden in nqp-land. Control-exceptions are clearly not an implementation details. So there is no reason for that limitation. Let’s remove it.
given 'a' {
succeed .Str;
CONTROL {
when CX::Succeed {
use nqp;
my $vmex := nqp::getattr(nqp::decont($_), Exception, '$!ex');
my $payload := nqp::getpayload($vmex);
say 'seen succeed with payload: ', $payload;
}
default { say 'seen ', .^name; }
}
}
# OUTPUT: seen succeed with payload: a
My expedition into nqp-land where started by the discovery, that CX::Succseed
and CX::Proceed
are swallowed by a hidden monster.
given 'a' {
CONTROL { default { say 'seen ', .^name } }
when Str { say 'Str'; succeed('It was a string.'); }
}
# OUTPUT:
$ less -N src/Perl6/Actions.nqp
9932 sub when_handler_helper($when_block) {
9933 unless nqp::existskey(%*HANDLERS, 'SUCCEED') {
9934 %*HANDLERS<SUCCEED> := QAST::Op.new(
9935 :op('p6return'),
9936 wrap_return_type_check(
9937 QAST::Op.new(
9938 :op('getpayload'),
9939 QAST::Op.new( :op('exception') )
9940 ),
9941 $*DECLARAND) );
The first when
or default
clause add a fresh handler and only checks for SUCCEED
and ignores any CONTROL
blocks already present. Given that intercepting X::Control
is specced, this is rather surprising.
Alas, adding exception handlers via macros, doesn’t work right now. This is not a pressing issue because macros are subject to change my RakuAST anyway and I might get the desired result with a Slang.
-
March 1, 2021 at 19:092021.09 Best of Raku? – Rakudo Weekly News