sub a { my @c = 1, 2, 3, 4; return @c };
sub b { my $d = [ 1, 2, 3, 4 ]; return $d };
say a().WHAT; # (Array)
say b().WHAT; # (Array)
my @e = a();
my @f = b();
say @e; # [1 2 3 4]
say @e[1]; # 2
say @f; # [[1 2 3 4]]
say @f[1]; # (Any)
# valid raku code, no errors messages
These subroutines both return an array, but the returned arrays behave differently. What would be the correct expression to make clear in the documentation which array type is returned by the subroutine?
sub b ( --> Array ) { my $d = [ 1, 2, 3, 4 ]; return $d };
say b().WHAT; # (Array)
sub b ( --> Scalar ) { my $d = [ 1, 2, 3, 4 ]; return $d };
say b().WHAT;
# Type check failed for return value; expected Scalar but got Array ([1, 2, 3, 4])
# in sub b at ...
# in block <unit> at ...
Keypoint 1: Method returning the array must have the return type as an array of the same data type as that of the array being returned. The return type may be the usual Integer, Double, Character, String, or user-defined class objects as well.
In computer programming, the return type (or result type) defines and constrains the data type of the value returned from a subroutine or method. In many programming languages (especially statically-typed programming languages such as C, C++, Java) the return type must be explicitly specified when declaring a function.
C programming does not allow to return an entire array as an argument to a function. However, you can return a pointer to an array by specifying the array's name without an index.
A method can return a reference to an array. The return type of a method must be declared as an array of the correct data type.
This is all about a Scalar
that hasn't decontainerized.
return $d
to return @$d
One option to get the same behavior is to alter the b
routine.
You have written "These subroutines both return an array, but the returned arrays behave differently.". But, as Holli notes, b
instead returns the Scalar
bound to $d
(that in turn contains an array):
sub b { my $d = [ 1, 2, 3, 4 ]; return $d };
say b().VAR.WHAT; # (Scalar)
You could change that by "decontainerizing" $d
, eg by prepending @
:
sub b { my $d = [ 1, 2, 3, 4 ]; return @$d };
say b().VAR.WHAT; # (Array)
my @f = b();
say @f; # [1 2 3 4]
say @f[1]; # 2
@f = b()
to @f = b[]
If you don't decontainerize the value returned by b
, then a second issue / opportunity arises.
Regardless of what b
returns, the @f
assignment will evaluate the list of values being assigned to it. In a list context, Scalar
s are left as is (just as they were with a plain return $d
). So if you do not change b
to decontainerize, then you'll need to do so in the assignment to @f
instead, if you want @e
and @f
to end up the same.
This time you can't just prepend @
to do so. Because that would spell @b
-- which Raku would interpret as an @b
variable.
One option is to write @f = @(b())
, but that would be ugly / non-idiomatic. Another option is to write @f = b[]
. This takes advantage of the fact that the parens in the b
calls were redundant. Appending []
(a "zen slice") has the same effect as writing @(b)
, but is one character less.
So, to decontainerize in the list assignment, you could write:
sub b { my $d = [ 1, 2, 3, 4 ]; return $d };
say b().VAR.WHAT; # (Scalar)
my @f = b[];
say @f; # [1 2 3 4]
say @f[1]; # 2
What would be the correct expression to make clear in the documentation
which array typewhat is returned by the subroutine?
I'm not sure what you mean by this question, even with the switch to merely "what is returned".
I'm also not sure what to point you to in the doc, and even if there is anywhere good to point you to, relative to the scenario in your SO.
I do know that, if it were me, I'd find the following doc sections confusing, relative to your scenario:
The Scalar containers and listy things section Holli linked. That section seems to me to currently be about use of Scalar
containers inside lists/arrays, which is relevant for the second issue I wrote about above ($d
is in a list on the rhs of the assignment to @f
). But that's not relevant to the first issue I wrote about (return $d
from the b
routine). There things are the other way around, namely there's an array inside a Scalar
.
The Scalar containers section earlier on the same page. The opening sentence -- "Although objects of type Scalar
are everywhere in Raku, you rarely see them directly as objects, because most operations decontainerize ..." works for me. But "a routine can return a container if it is marked as is rw
" is more problematic. It is true:
my $x = 23;
sub f() is rw { $x };
f() = 42;
say $x; # OUTPUT: «42»
But one doesn't have to mark a routine is rw
to return a container. One can use the return
routine, as you did:
my $x = 23;
sub f() { return $x };
say f().VAR.WHAT; # OUTPUT: «Scalar»
The original question asks why the following error occurs:
sub b ( --> Scalar ) { my $d = [ 1, 2, 3, 4 ]; return $d };
say b().WHAT;
# Type check failed for return value; expected Scalar but got Array ([1, 2, 3, 4])
# in sub b at ...
# in block <unit> at ...
This answer is intended to elaborate on constraints of the return type as documented here
If you do this, now you are returning a Scalar:
sub b( --> Scalar ) { my $x=42; my $d = $x; return $d };
say b().WHAT;
You get a different error
Type check failed for return value; expected Scalar but got Int (42)
in sub b at scum.raku line 2
in block <unit> at scum.raku line 7
What is going on here?
Well raku automatically decontainerizes the Scalar $x before testing the type - this is how Scalar containers work. So run of the mill code you should be using concrete return type constraints like Int, Real, Num, IntStr, Allomorph, Cool and so on. Raku built in types are beautifully crafted to give the coder precise control of the level of this test.
BUT - if you want to do power tricks, then you can apply the --> Scalar
constraint like this (as mentioned by @raiph):
sub b( --> Scalar ) { my $x=42; my $d = $x.VAR; return $d };
say b().WHAT; #(Scalar)
In other words, the meta method .VAR bypasses the normal transparent decont of Scalar containers to let you test whether or not your return type is top level a container.
So (as I am sure you have gathered from raiph's better answer) please document --> Scalar
as a rare form as opposed to the common --> Array
check.
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