Home > Raku > Spying on return

Spying on return

Matthew Stuckwisch wrote a module to help with spying on return values. That made me wonder if one could wrap return.

&return.wrap: -> \c { say c; CX::Return.new.throw }
sub s { return 42; }
s;

Output:

42
True
===SORRY!===
control exception without handler
Nil
Some exceptions were thrown in END blocks:
3
Unhandled exception: Too many positionals passed; expected 1 argument but got 2
   at SETTING::src/core.c/Exception.pm6:510  (/usr/local/src/rakudo/install/share/perl6/runtime/CORE.c.setting.moarvm:print_exception)
 from SETTING::src/core.c/Exception.pm6:566  (/usr/local/src/rakudo/install/share/perl6/runtime/CORE.c.setting.moarvm:)

That made me laugh, because Rakudo actually tried to do what I asked it to. By wrapping return we globally change that function. It can’t work because the control exception is thrown in the wrong block. But it doesn’t blow up completely because CORE doesn’t use return.

Since return is an ordinary Sub, there must be a way to throw in the right context.

my &return = -> \c {
    say c;
    use nqp;
    nqp::throwpayloadlexcaller(nqp::const::CONTROL_RETURN, nqp::p6recont_ro(c));
}

sub s { return 42; }
s;

# OUTPUT: 42

That works, is neat but not very helpful. Most routines don’t use a return-statement. To get hold of any return value, we need to wrap the Callable in question. We can use a trait for that.

sub trait_mod<is>:(Callable $c, :$return-spy!) {
    $c.wrap: sub (|c) {
        my \ret = callsame;
        say sprintf('%s(%s): %s', $c.name, c, ret) if $*debug;
        return-rw ret
    };
}

sub f is return-spy { 42 }
my $*debug = True;
f;

# OUTPUT: 42

We could move the conditional of $*debug out of the wrapper to avoid the runtime penalty if no debugging is needed. I’m not sure if that would work well with precomp though. Please note the return-rw.

sub b(\c) { return-rw c }
my $c = 1;
b($c)++; # this won't work without return-rw
say $c;
# OUTPUT: 2

With a simple return, even a sigilless argument will not maintain the container. In fact, using return-rw is almost always required for debugging subs. I left a note with Matthew.

Categories: Raku