Intermediate

Functions in Raku

Learn how to define and call subroutines in Raku, including typed signatures, default and named parameters, recursion, multiple dispatch, and higher-order functions

Functions are where Raku’s multi-paradigm design really shines. A “function” in Raku is a subroutine (sub), and subroutines are first-class values: you can store them in variables, pass them as arguments, and return them from other functions. On top of that, Raku layers a remarkably rich signature system—typed parameters, defaults, named arguments, optional values, slurpy lists—and multiple dispatch, where several routines can share a name and Raku picks the right one based on the arguments.

Because Raku is gradually typed, you decide how much structure to impose. You can write a quick untyped sub for a prototype, then tighten it later with type constraints and a return type without rewriting the call sites. The signature is also documentation: a reader sees exactly what a routine expects and produces.

In this tutorial you’ll learn how to define subroutines, work with positional, default, named, and optional parameters, understand lexical scope, write recursive functions (including the elegant multiple-dispatch style), and treat functions as data with closures and higher-order routines.

Defining and Calling Subroutines

A subroutine is declared with sub. Parameters live in a signature inside parentheses, the return type follows -->, and the last expression is returned implicitly. Parameters can carry type constraints, default values, or a : prefix to make them named instead of positional.

Create a file named functions.raku:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Defining and calling subroutines in Raku

# Positional parameters with types and an explicit return type
sub area(Numeric $width, Numeric $height --> Numeric) {
    $width * $height
}
say area(4, 5);                 # 20

# Default parameter values
sub greet($name, $greeting = "Hello") {
    "$greeting, $name!"
}
say greet("Raku");              # uses the default greeting
say greet("Raku", "Hi");        # overrides the default

# Named parameters use a colon prefix in the signature
sub power(:$base, :$exp = 2) {
    $base ** $exp
}
say power(base => 3);           # exp defaults to 2
say power(:base(2), :exp(10));  # both supplied

# Optional parameters end with a trailing ?
sub label($text, $suffix?) {
    $suffix.defined ?? "$text ($suffix)" !! $text
}
say label("Item");              # no suffix
say label("Item", "new");       # with suffix

Notice how named parameters free the caller from remembering argument order, and how $suffix.defined lets the routine react to whether an optional argument was actually passed. The ?? !! construct is Raku’s ternary conditional.

Recursion, Scope, and Multiple Dispatch

Recursion is natural in Raku. Even more idiomatic is multiple dispatch: declaring several multi routines with the same name but different signatures—including literal values—so Raku selects the matching one. This often replaces explicit base-case conditionals.

Variables declared with my are lexically scoped: visible only within their enclosing block, but a file-level my is visible to subroutines defined alongside it.

Create a file named recursion.raku:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# Recursion, scope, and multiple dispatch in Raku

# Classic recursive factorial
sub factorial(Int $n --> Int) {
    $n <= 1 ?? 1 !! $n * factorial($n - 1)
}
say factorial(5);            # 120

# Fibonacci using multiple dispatch on literal values
multi fib(0) { 0 }
multi fib(1) { 1 }
multi fib(Int $n) { fib($n - 1) + fib($n - 2) }
say fib(10);                 # 55

# Lexical scope: file-level 'my' is visible to subs below it
my $outer = "global";
sub show-scope {
    my $inner = "local";     # visible only inside this sub
    "$outer / $inner"
}
say show-scope();            # global / local

The three multi fib definitions read almost like a mathematical specification: two base cases and one recursive rule. Raku dispatches fib(0) and fib(1) to the literal variants and everything else to the Int $n variant.

Functions as Values: Closures and Higher-Order Routines

Subroutines are first-class. Pointy blocks (-> $x { ... }) create anonymous functions, the & sigil holds a callable, and routines like map, grep, and reduce accept other functions as arguments. A subroutine that returns a block closes over the variables in scope, producing a closure.

Create a file named higher_order.raku:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Higher-order functions and first-class subs in Raku

# Subs are values you can store in a variable
my &double = -> $x { $x * 2 };
say double(21);                   # 42

# Pass blocks to map and grep
my @nums = 1, 2, 3, 4, 5, 6;
say @nums.map({ $_ ** 2 });       # square each element
say @nums.grep({ $_ %% 2 });      # keep even values (%% is "divisible by")

# Reduce a list to a single value with the + reduction
say @nums.reduce(&[+]);           # sum

# Return a closure that captures its environment
sub make-adder($n) {
    return -> $x { $x + $n };
}
my &add5 = make-adder(5);
say add5(10);                     # 15

# The Whatever-star (*) builds quick anonymous functions
say (1..5).map(* + 100);          # add 100 to each

Here make-adder(5) returns a function that “remembers” $n = 5, and * + 100 is shorthand for -> $x { $x + 100 }—the Whatever-star turns an expression into a one-argument function automatically.

Running with Docker

Run each example with the official rakudo-star:alpine image. The $(pwd) mount makes the current directory available inside the container at /app.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Pull the official image
docker pull rakudo-star:alpine

# Run the subroutine basics example
docker run --rm -v $(pwd):/app -w /app rakudo-star:alpine raku functions.raku

# Run the recursion and multiple-dispatch example
docker run --rm -v $(pwd):/app -w /app rakudo-star:alpine raku recursion.raku

# Run the higher-order functions example
docker run --rm -v $(pwd):/app -w /app rakudo-star:alpine raku higher_order.raku

Expected Output

Running functions.raku:

20
Hello, Raku!
Hi, Raku!
9
1024
Item
Item (new)

Running recursion.raku:

120
55
global / local

Running higher_order.raku:

42
(1 4 9 16 25 36)
(2 4 6)
21
15
(101 102 103 104 105)

Key Concepts

  • sub defines a subroutine, and the last expression is returned implicitly—no return keyword is required unless you want an early exit.
  • Signatures are powerful: positional, typed, default ($x = value), named (:$x), optional ($x?), and slurpy (*@x) parameters all combine freely.
  • Return types via --> document and (with typed signatures) enforce what a routine produces, fitting Raku’s gradual typing.
  • Multiple dispatch (multi) lets several routines share a name and dispatch on argument types or even literal values—often clearer than nested conditionals.
  • Lexical scope with my keeps variables confined to their block; closures capture the surrounding environment when a routine returns a block.
  • Functions are first-class values: store them with the & sigil, pass them to map/grep/reduce, and build them quickly with pointy blocks or the Whatever-star (*).
  • %% tests divisibility and ??/!! is the ternary operator—small Raku conveniences that make functional code concise.

Running Today

All examples can be run using Docker:

docker pull rakudo-star:alpine
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining