raku.gg / beginner

Lists and Arrays

2026-03-14

Lists and arrays are the workhorses of most programs. Raku treats them with special care, giving you both a rich set of built-in methods and functional programming tools like map, grep, and reduce. In this tutorial, we will cover everything from basic array creation to powerful list transformations.

Lists vs Arrays

Raku distinguishes between Lists (immutable sequences) and Arrays (mutable containers). In practice, the difference is subtle for beginners, but worth knowing:
my @array = 1, 2, 3; # Array (mutable) my $list = (1, 2, 3); # List (stored in a scalar) @array.push(4); # works: [1 2 3 4] # $list.push(4); # ERROR: Cannot call 'push' on an immutable List
When you use the @ sigil, you get a mutable Array. Most of the time, this is what you want.

Creating Arrays

# From a comma-separated list my @numbers = 1, 2, 3, 4, 5; # From a range my @digits = 0..9; # From the quote-words construct my @colors = <;red green blue yellow>;; # An empty array my @empty; say @empty.elems; # 0 # With repetition my @zeros = 0 xx 10; say @zeros; # [0 0 0 0 0 0 0 0 0 0]
The xx operator is the list repetition operator, creating a list with the value repeated N times.

Accessing Elements

my @fruits = <;apple banana cherry date elderberry>;; say @fruits[0]; # apple (first element) say @fruits[1]; # banana say @fruits[*-1]; # elderberry (last element) say @fruits[*-2]; # date (second to last)
The -1 syntax uses the Whatever star to count from the end. This is clean and avoids off-by-one errors.

Array Slices

You can grab multiple elements at once:
my @letters = 'a'..'j'; say @letters[0, 2, 4]; # (a c e) say @letters[0..3]; # (a b c d) say @letters[0, 2 ... 8]; # (a c e g i) sequence!

Modifying Arrays: push, pop, shift, unshift

These four methods add and remove elements from either end:
my @stack; # push adds to the end @stack.push(1); @stack.push(2, 3); say @stack; # [1 2 3] # pop removes from the end my $top = @stack.pop; say $top; # 3 say @stack; # [1 2] # unshift adds to the beginning @stack.unshift(0); say @stack; # [0 1 2] # shift removes from the beginning my $first = @stack.shift; say $first; # 0 say @stack; # [1 2]
Think of it this way:
  • push/pop work on the right end (like a stack)
  • unshift/shift work on the left end (like a queue)

append and prepend

append is like push but flattens arrays:
my @a = 1, 2, 3; @a.push([4, 5]); # push adds array as single element say @a; # [1 2 3 [4 5]] my @b = 1, 2, 3; @b.append(4, 5); # append flattens say @b; # [1 2 3 4 5] my @c = 4, 5, 6; @c.prepend(1, 2, 3); say @c; # [1 2 3 4 5 6]

.elems, .end, and Boolean Context

my @items = <;foo bar baz>;; say @items.elems; # 3 (number of elements) say @items.end; # 2 (index of last element) say ?@items; # True (non-empty array is truthy) say !@items; # False my @empty; say ?@empty; # False (empty array is falsy)

Sorting

The .sort method returns a new sorted list:
my @nums = 5, 3, 8, 1, 9, 2; say @nums.sort; # (1 2 3 5 8 9) my @words = <;banana apple cherry date>;; say @words.sort; # (apple banana cherry date)
Custom sort with a comparison block:
# Sort numbers in descending order my @nums = 5, 3, 8, 1, 9, 2; say @nums.sort({ $^b <;=> $^a }); # (9 8 5 3 2 1) # Sort strings by length my @words = <;cat elephant bee dog>;; say @words.sort({ $^a.chars <;=> $^b.chars }); # (cat bee dog elephant) # Sort by a computed property my @files = <;README.md app.js style.css index.html>;; say @files.sort(*.chars); # sorted by filename length
The
.chars syntax is a WhateverCode block, equivalent to { $_.chars }.

Reversing

my @nums = 1, 2, 3, 4, 5; say @nums.reverse; # (5 4 3 2 1) # The original is unchanged say @nums; # [1 2 3 4 5]

grep: Filtering

.grep returns elements that match a condition:
my @nums = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10; say @nums.grep(* >; 5); # (6 7 8 9 10) say @nums.grep(* %% 2); # (2 4 6 8 10) even numbers say @nums.grep(3..7); # (3 4 5 6 7) range match my @words = <;apple banana avocado cherry apricot>;; say @words.grep(/^a/); # (apple avocado apricot) say @words.grep(*.chars >; 5); # (banana avocado cherry apricot)
.grep uses smart matching, so you can pass types, regexes, ranges, junctions, blocks, or plain values.

map: Transforming

.map applies a function to every element and returns the results:
my @nums = 1, 2, 3, 4, 5; say @nums.map(* * 2); # (2 4 6 8 10) say @nums.map(* ** 2); # (1 4 9 16 25) say @nums.map({ "$_ squared is { $_ ** 2 }" }); # (1 squared is 1 2 squared is 4 3 squared is 9 ...) my @words = <;hello world>;; say @words.map(*.uc); # (HELLO WORLD) say @words.map(*.chars); # (5 5)

Chaining map and grep

This is where functional programming shines. Chain methods together to build data pipelines:
my @data = 1..20; # Get even numbers, square them, take the first 5 my @result = @data .grep(* %% 2) .map(* ** 2) .head(5); say @result; # (4 16 36 64 100)

flat: Flattening Nested Lists

my @nested = (1, 2), (3, 4), (5, 6); say @nested.flat; # (1 2 3 4 5 6) my @deep = (1, (2, (3, 4))); say @deep.flat; # (1 2 3 4)

unique and squish

.unique removes duplicate values:
my @nums = 1, 2, 2, 3, 3, 3, 4, 4, 4, 4; say @nums.unique; # (1 2 3 4) my @words = <;cat dog cat bird dog>;; say @words.unique; # (cat dog bird)
.squish removes consecutive duplicates (useful for sorted data or run-length scenarios):
my @data = 1, 1, 2, 2, 2, 1, 1, 3, 3; say @data.squish; # (1 2 1 3) say @data.unique; # (1 2 3)

reduce: Combining All Elements

.reduce combines all elements using an operation:
my @nums = 1, 2, 3, 4, 5; say @nums.reduce(&infix:<+>); # 15 (sum) say @nums.reduce(&infix:<*>); # 120 (product) # Or use the [operator] syntax: say [+] @nums; # 15 say [*] @nums; # 120 say [~] <;a b c>;; # abc (string concatenation) say [max] @nums; # 5 say [min] @nums; # 1
The [operator] reduction meta-operator is one of Raku's most elegant features.

head and tail

Grab elements from the beginning or end:
my @nums = 1..10; say @nums.head(3); # (1 2 3) say @nums.tail(3); # (8 9 10) say @nums.head; # 1 (single element) say @nums.tail; # 10

Ranges as Arrays

Ranges are lazy. They become arrays when you need them to:
my @evens = (0, 2 ... 20); say @evens; # [0 2 4 6 8 10 12 14 16 18 20] # Use ^N for 0 up to (but not including) N for ^5 { say $_ } # 0, 1, 2, 3, 4 my @indices = ^10; say @indices; # [0 1 2 3 4 5 6 7 8 9]
The ^N syntax is shorthand for 0..^N and is used constantly in Raku for index ranges.

zip: Combining Arrays

The zip function pairs up elements from multiple arrays:
my @names = <;Alice Bob Charlie>;; my @scores = 95, 87, 92; for @names Z @scores -> ($name, $score) { say "$name: $score"; } # Alice: 95 # Bob: 87 # Charlie: 92
You can also zip with an operator:
my @a = 1, 2, 3; my @b = 10, 20, 30; say @a Z+ @b; # (11 22 33) element-wise addition say @a Z~ @b; # (110 220 330) element-wise concatenation

Practical Example: Student Grades

#!/usr/bin/env raku my @students = ( { name => "Alice", grades => [92, 88, 95, 91] }, { name => "Bob", grades => [78, 82, 76, 80] }, { name => "Charlie", grades => [95, 97, 93, 98] }, { name => "Diana", grades => [65, 70, 68, 72] }, ); # Calculate averages and sort by performance my @results = @students .map({ my $avg = ([+] $_<;grades>;) / $_<;grades>;.elems; { name => $_<;name>;, average => $avg } }) .sort({ $^b<;average>; <;=> $^a<;average>; }); # descending say "=== Grade Report ==="; for @results -> %r { my $bar = "#" x (%r<;average>; / 5).Int; say sprintf("%-10s %5.1f %s", %r<;name>;, %r<;average>;, $bar); } my @averages = @results.map(*<average>;); say "\nClass average: { ([+] @averages) / @averages.elems }"; say "Highest: { @averages.max }"; say "Lowest: { @averages.min }";

What is Next?

With lists and arrays mastered, the natural companion is hashes. Next, we will explore Raku's hash and Pair system for working with key-value data.