confusion about lists contained in an aggregate, maybe context problem?

for doesn't loop over itemized values.

When you place something in a scalar container it gets itemized.

sub foo ( $v ) { # itemized
  for $v { .say }
}
sub bar ( \v ) {
  for v { .say }
}

foo (1,2,3);
# (1 2 3)

bar (1,2,3);
# 1
# 2
# 3

An element in a Hash is also a scalar container.

my %h = 'foo' => 'bar';

say %h<foo>.VAR.^name;
# Scalar

So if you place a list into a Hash, it will get itemized.

my %h;

my \list = (1,2,3);
%h<list> = list;

say list.VAR.^name;
# List
say %h<list>.VAR.^name;
# Scalar

So if you want to loop over the values you have to de-itemize it.

%h<list>[]
%h<list><>
%h<list>.list
%h<list>.self

@(%h<list>)

given %h<list> -> @list { … }

my @list := %h<list>;

(my @ := %h<list>)  # inline version of previous example

You could avoid this scalar container by binding instead.

%h<list> := list;

(This prevents the = operator from working on that hash element.)


If you noticed that in the class object you defined it with an @ not $

class R1 {
    has Str $.some-str is required;
    has @.some-list is required;
}

If you changed it to an $ and mark it rw it will work like the Hash example

class R2 {
    has Str $.some-str is required;
    has List $.some-list is required is rw;
}

my $r2 = R2.new(
    some-str => '…',
    some-list => (1,2,3),
);

for $r2.some-list { .say }
# (1 2 3)

It has to be a $ variable or it won't be in a Scalar container.
It also has to be marked rw so that the accessor returns the actual Scalar container rather than the de-itemized value.


This has nothing to do with [] versus (). This has to do with the difference between $ (indicating an item) and % (indicating an Associative):

sub a(%h) { dd %h }       # a sub taking an Associative
sub b(Hash $h) { dd $h }  # a sub taking an item of type Hash

a { a => 42 };  # Hash % = {:a(42)}
b { a => 42 };  # ${:a(42)}

In the "b" case, what is received is an item. If you try to iterate over that, you will get 1 iteration, for that item. Whereas in the "a" case, you've indicated that it is something Associative that you want (with the % sigil).

Perhaps a clearer example:

my $a = (1,2,3);
for $a { dd $_ }  # List $a = $(1, 2, 3)␤

Since $a is an item, you get one iteration. You can indicate that you want to iterate on the underlying thing, by adding .list:

for $a.list { dd $_ }  # 1␤2␤3␤

Or, if you want to get more linenoisy, prefix a @:

for @$a { dd $_ }  # 1␤2␤3␤

Not strictly an answer, but an observation: in Raku, it pays to use classes rather than hashes, contrary to Perl:

my %h = a => 42, b => 666;
for ^10000000 { my $a = %h<a> }
say now - INIT now;  # 0.4434793

Using classes and objects:

class A { has $.a; has $.b }
my $h = A.new(a => 42, b => 666);
for ^10000000 { my $a = $h.a }
say now - INIT now;  # 0.368659

Not only is using classes faster, it also prevents you from making typos in initialization if you add the is required trait:

class A { has $.a is required; has $.b is required }
A.new(a => 42, B => 666);
# The attribute '$!b' is required, but you did not provide a value for it.

And it prevents you from making typos when accessing it:

my $a = A.new(a => 42, b => 666);
$a.bb;
# No such method 'bb' for invocant of type 'A'. Did you mean 'b'?

Tags:

Raku