Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to specify a returned Array type?

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 ...
like image 840
sid_com Avatar asked Aug 08 '21 17:08

sid_com


People also ask

What is the return type of an array?

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.

How do you define return type?

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.

What is the return type of array in C?

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.

Can methods have an array as a return type?

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.


2 Answers

This is all about a Scalar that hasn't decontainerized.

You could change 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

Or change @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, Scalars 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

"to make clear in the documentation"

What would be the correct expression to make clear in the documentation which array type what 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␤»
like image 169
raiph Avatar answered Oct 21 '22 23:10

raiph


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.

like image 37
p6steve Avatar answered Oct 21 '22 23:10

p6steve