raku.gg / functional

Lazy Lists and Sequences

2026-03-23

Raku embraces laziness. Rather than computing every element upfront, lazy lists generate values on demand. This lets you work with infinite sequences, process huge datasets without blowing up memory, and write elegant functional code.

Lazy Ranges

Ranges in Raku are lazy by default when they are unbounded:
my @infinite = 1 .. *; # Infinite range -- lazy say @infinite[0..4]; # (1 2 3 4 5) say @infinite[99]; # 100 my @finite = 1 .. 1000; # Finite range -- also lazy until reified
The * (Whatever) makes a range infinite. No memory is allocated for elements that have not been accessed yet.

Checking Laziness

You can check whether a list is lazy with the .is-lazy method:
say (1..*).is-lazy; # True say (1..100).is-lazy; # False say (1..*).grep(*.is-prime).is-lazy; # True

The Sequence Operator (...)

The ... operator is Raku's most versatile tool for creating sequences. It infers patterns:
# Arithmetic sequences say (1, 3, 5 ... 19); # (1 3 5 7 9 11 13 15 17 19) say (0, 10, 20 ... 100); # (0 10 20 30 40 50 60 70 80 90 100) # Geometric sequences say (1, 2, 4 ... 256); # (1 2 4 8 16 32 64 128 256) say (1000, 100, 10 ... 0.01);# (1000 100 10 1 0.1 0.01) # Fibonacci say (1, 1, *+* ... *)[^10]; # (1 1 2 3 5 8 13 21 34 55) # Custom formula say (1, { $_ * 2 + 1 } ... *)[^8]; # (1 3 7 15 31 63 127 255)
The ... operator takes seed values and a generator (or infers one from the pattern), producing a lazy sequence.

Infinite Sequences

Because sequences are lazy, infinite ones are perfectly fine:
# All natural numbers my @nat = 0, 1, 2 ... *; # All squares my @squares = (0, 1, 4 ... *); # Hmm, this won't infer correctly my @squares2 = (^Inf).map(* ** 2); # Better # All Fibonacci numbers my @fib = 1, 1, *+* ... *; say @fib[^15]; # (1 1 2 3 5 8 13 21 34 55 89 144 233 377 610) # Powers of 2 my @pow2 = 1, 2, 4 ... *; say @pow2[^10]; # (1 2 4 8 16 32 64 128 256 512)
You just take what you need with [^N] or .head(N).

gather/take

The gather/take construct is Raku's coroutine-style generator. It creates a lazy list where you explicitly yield values:
my @evens = gather { my $n = 0; loop { take $n; $n += 2; } }; say @evens[^10]; # (0 2 4 6 8 10 12 14 16 18)
The gather block runs lazily. Execution pauses at each take until the next value is requested.

Practical gather/take

Here is a more practical example, generating a filtered stream:
sub primes() { gather { for 2..* -> $candidate { take $candidate if $candidate.is-prime; } } } say primes()[^20]; # (2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71)
And a tree traversal:
class Node { has $.value; has Node $.left; has Node $.right; } sub inorder(Node $n) { gather { if $n.left { take $_ for inorder($n.left) } take $n.value; if $n.right { take $_ for inorder($n.right) } } } my $tree = Node.new( value => 4, left => Node.new( value => 2, left => Node.new(value => 1), right => Node.new(value => 3), ), right => Node.new(value => 5), ); say inorder($tree); # (1 2 3 4 5)

Lazy Operations Chain

Most list operations preserve laziness:
my @result = (1..*) .grep(*.is-prime) # Still lazy .map(* ** 2) # Still lazy .head(10); # Takes first 10 say @result; # (4 9 25 49 121 169 289 361 529 625) -- first 10 squared primes
This pipeline does not compute a single value until .head(10) pulls them through.

Controlling Reification

Sometimes you need to force a lazy list to fully evaluate (reify):
my @lazy = (1..*).grep(*.is-prime).head(100); # .eager forces full evaluation my @eager = @lazy.eager; # .cache ensures values are computed once and stored my @cached = (1..*).grep(*.is-prime).head(100).cache;
Be careful: calling .eager on an infinite list will hang forever.

Lazy File Processing

Laziness is great for processing large files line by line:
# lines() is lazy -- reads one line at a time for "large-file.txt".IO.lines -> $line { say $line if $line.contains("ERROR"); } # Process a huge file without loading it all into memory my $error-count = "large-file.txt".IO.lines.grep(/ERROR/).elems; say "Found $error-count errors";

Zipping Lazy Sequences

You can combine lazy sequences:
my @letters = 'a' .. 'z'; my @numbers = 1 .. *; for @letters Z @numbers -> ($l, $n) { say "$l => $n"; } # a => 1, b => 2, ... z => 26 # Stops naturally when @letters is exhausted

Tips

Laziness is a fundamental part of Raku's design philosophy. It makes your code more memory-efficient and often more expressive.