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 D
efined).
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.
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...
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;
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With