Containers, itemisation, and why my list has one element
Every snippet on this page was run on both Raku++ and Rakudo and produces identical output.
This is the single most confusing corner of Raku, and it is not a Raku++ quirk — Rakudo behaves the same way. It is worth understanding once.
Why is my @a = %h<k> one element?
my %h;
my @x = 7, 8, 9;
%h<k> = @x;
my @a = %h<k>;
say @a.elems; # 1 ← not 3
Because a hash value lives in a Scalar container. Anything read out of one is itemised: it behaves as a single thing in list context, however many elements it contains. The same is true of a scalar variable:
my @x = 7, 8, 9;
my $s = @x;
my @b = $s;
say @b.elems; # 1
@x itself is not itemised, so it spreads:
my @x = 7, 8, 9;
my @c = @x;
say @c.elems; # 3
How do I get the elements out?
Any of these — they all decontainerise:
my %h; %h<k> = [7, 8, 9];
my @a = @(%h<k>); # the @ contextualiser
my @b = %h<k>.list;
my @c = %h<k>.Array;
my @d = |%h<k>; # flattening prefix
say @a.elems, @b.elems, @c.elems, @d.elems; # 3333
@(…) is the usual one. Reach for .Array when you want a real Array back (a mutable copy), .list when a List is fine.
What makes something itemised?
The $ sigil, in all its forms:
say (1, 2, 3).elems; # 3
say $(1, 2, 3).elems; # 1 — $() itemises
my @flat = (1, 2), (3, 4);
say @flat.elems; # 2 — each inner list is one element
say [1, 2].elems; # 2 — [] is an Array, not itemised
And it survives into .raku, which is how you can see it:
my %h; %h<k> = [1, 2];
say %h<k>.raku; # $[1, 2] ← the $ marks the itemisation
say @(%h<k>).raku; # [1, 2]
Passing a list to a routine
An itemised value arrives as one argument:
sub count(*@a) { @a.elems }
my $items = (1, 2, 3);
say count($items); # 1
say count(|$items); # 3 — | flattens it into the argument list
say count(@($items)); # 3
This is the usual cause of "my sub got one argument and I passed three things".
Attributes follow their sigil
An attribute's sigil is a container type, so the value is coerced to match:
class T {
has @.list = (1, 2); # a List initialiser…
has %.h = (a => 1);
}
say T.new.list.WHAT; # (Array) ← …but the attribute is an Array
say T.new.h.WHAT; # (Hash)
That matters when you render one — an Array reprs as [1, 2] where a List would repr as (1, 2).
Gotchas
.elems and binding can disagree if a coercion is wrong. $v.Array.elems must equal (my @a = $v.Array).elems — if they differ, that is a bug, not a subtlety. (Raku++ had exactly that bug before v1.2.5: .Array reported 3 and bound 1.)
A for loop binding itemises. Iterating pairs of values binds each into a scalar, so a nested list stays whole:
for [1, 2], [3, 4] -> $pair { say $pair.elems } # 2, then 2
.flat stops at an itemised value. That is what it is for — itemisation is the mechanism that lets a structure survive flattening.
---
Back to the FAQ index.