Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explicitly named return values?

In Go (and G++?), the following is a common idiom:

func sqr(x int) (n int, err error) {
    n = x * x
    err = nil
    return
}

The (n int, err error) explicitly enumerates the name and type of the return values. There are in my opinion lots of advantages to this, and I like it.


In Perl 6, we can:

my sub sqr (Int:D $x) returns Int:D {
    $x ** 2;
}

The return is implicit, which makes me kind of uncomfortable (we could make it explicit with return), but you may notice the return type is specified (as is the fact it's Defined).

Unsurprisingly, there's no obvious way to return a value explicitly by name, but I'm curious, since Perl (especially 6) is extensively modifiable in every way, if there's a way to implement this.1


1 However hacky it may be, but too hacky and I'd avoid using it for "real" things.

like image 685
cat Avatar asked Dec 18 '22 20:12

cat


2 Answers

If all else fails, you can always write your own slang.

However, here are two less involved approaches I came up with, the first one using a dynamic variable of fixed name, the second one using an rw parameter of user-defined name:

multi sub trait_mod:<is>(Routine:D \r, :$dynrv!) {
    r.wrap(-> | { my $*rv; callsame; $*rv })
}

multi sub trait_mod:<is>(Routine:D \r, :$pararv!) {
    r.wrap(-> |c { my $rv; callwith(|c, $rv); $rv })
}

sub double($x) is dynrv {
    $*rv = $x * 2;
    return 666; # discarded
}

sub triple($x, $y is rw) is pararv {
    $y = $x * 3;
    return 666; # discarded
}

say double 42;
say triple 42;

Note that this only supports a single return value, though I have some ideas how multiple ones could be made to work...


edit: Eg this way:

multi sub trait_mod:<is>(Routine:D \r, :@dynrv!) {
    r.wrap(-> | {
        my @rv = Nil xx @dynrv;
        my $*rv = Map.new(@dynrv Z=> @rv);
        callsame;
        @dynrv > 1 ?? @rv !! @rv[0];
    })
}

multi sub trait_mod:<is>(Routine:D \r, Int :$pararv!) {
    r.wrap(-> |c {
        my @rv = Nil xx $pararv;
        callwith(|c, |@rv);
        $pararv > 1 ?? @rv !! @rv[0];
    })
}

sub divmod($a, $b) is dynrv<d m> {
    $*rv<d> = $a div $b;
    $*rv<m> = $a mod $b;
}

sub plusmin($a, $b, $p is rw, $m is rw) is pararv(2) {
    $p = $a + $b;
    $m = $a - $b;
}

say divmod 14, 3;
say plusmin 14, 3;
like image 105
Christoph Avatar answered Jan 08 '23 11:01

Christoph


While slangs can be used to get exactly what you want, I believe they're the wrong size solution to this problem. Firstly, cat's Go example is returning error information as well as an (Int). I'd suggest the Perl 6 way for that is to simply return a Fail object using &fail which the caller can test for using .defined or the // operator;

if $something_wrong {
  return fail "Dang!  Even the squaring function isn't working today."
}
else {
  return $x ** 2
}

I know this doesn't document the dual return values as requested, but do you want to use an explicit variable for exceptional out-of-band data? What if it wasn't error data but other information?
Let's assume we want to return an (Int) with an Easter-egg message. In this case, as the extra data is a different type, (Str), we can run-time mixin a role with a .Str method that returns the message if the return value is used in string context.

return $x * $x but "Suprise!  Bet you weren't expecting that!"

Alternatively, if the extra data is of the same type we can explicitly mixin an anonymous role. Let's say we want to return an (Int) with a .size property;

return $x but role { has $.size = calculate_size() }

Finally, you can document your return type explicitly by declaring the role and then using a subset to define your own (Int) type that demands the role;

role Size { has UInt:D $.size = 0 }
subset MyInt of Int where Size;

sub sqr(Int:D $x, UInt:D :$size = 12) returns MyInt:D {
  return $x * $x but Size($size)
}

my $square5 = sqr(5);
say "5 squared is $square5 with size $square5.size()."
# prints: 5 squared is 25 with size 12.

Details are in S14 and S12 - Anonymous mixin roles

like image 43
Marty Avatar answered Jan 08 '23 10:01

Marty