Custom when
I didn’t quite like the syntax of using match
in the last post. The commas in the list of its arguments looked strangely out of place. Maybe because my eyes are used to a given
block. Sleeping over it helped.
sub accord(&c) { (c(CALLER::<$_>); succeed) if &c.cando(\(CALLER::<$_>)) }
given Err.new(:msg<a>) {
accord -> Hold (:$key) { put „holding $key“; }
accord -> Err (:$msg) { warn „ERR: $msg“ }
default { fail ‚unsupported‘ }
}
This works because accord
mimics what when
is doing. It does some matching, calls a block when True
and adds a succeed
(by throwing a control exception) at the end of each block. All given
is doing is setting the topic. It also acts as a CALLER
so we can access its $_
via a pseudo package. Using the signature of a pointy to do deconstruction is quite powerful. Adding this to CORE might be a good idea.
We may have to change the definition of Rako to: “Raku is a highly composable programming language, where things just fall into place.”
UPDATE:
There are cases where $_
is not a dynamic. Also, succeed
is throwing a control exception and the handler for those are added by when
or default
. This happens at compile time and can’t currently be done with macros. The first problem is solvable with black magic. The latter requires a default
-block. I didn’t find a way to provide a sensible error message if that block is missing.
multi sub accord(&c) {
use nqp;
$_ := nqp::getlexcaller('$_');
(c($_); succeed) if &c.cando(\($_))
}
for @possibilities.roll(1) -> $needle {
given $needle {
accord -> Hold (:$key) { put „holding $key“; }
accord -> Err (:$msg) { warn „ERR: $msg“ }
default { warn ‚unsopported‘ }
}
}
I tried your code, `.cando(\($_))` doesn’t seem to work correctly. I changed it to:
sub accord (&c) {
use nqp;
my $capture = nqp::getlexcaller(‘$_’).List.Capture;
if &c.cando($capture) {
c(|$capture);
succeed;
}
}
If `leave` was implemented, maybe we could use that instead of `succeed`, given the behavior of `when`.
For error message I tried something hacky:
CATCH {
when X::ControlFlow {
my $interesting_index = .backtrace.next-interesting-index: :named;
die ‘No default block was provided’ if .illegal eq ‘succeed’ && $interesting_index == 2 && .backtrace[$interesting_index].subname eq &?ROUTINE.name;
}
}
I’m not sure now, `\($_)` is probably correct.