raku.gg / metaprogramming

Multi Dispatch

2026-03-27

Multi dispatch is one of Raku's most distinctive features. Instead of writing one function with a tangle of conditionals, you write multiple variants of the same function, each handling a specific case. Raku picks the right one at call time based on the arguments you pass.

Multi Subs

Declare multiple variants of a sub using the multi keyword:
multi sub greet(Str $name) { say "Hello, $name!"; } multi sub greet(Str $name, Int $times) { say "Hello, $name!" for ^$times; } multi sub greet() { say "Hello, stranger!"; } greet("Alice"); # Hello, Alice! greet("Bob", 3); # Hello, Bob! (x3) greet(); # Hello, stranger!
Raku examines the number and types of arguments and dispatches to the most specific matching candidate.

Type-Based Dispatch

The real power comes from dispatching on types:
multi sub describe(Int $n) { say "$n is an integer"; } multi sub describe(Str $s) { say "'$s' is a string of {$s.chars} characters"; } multi sub describe(Array $a) { say "An array with {$a.elems} elements"; } multi sub describe(Hash $h) { say "A hash with {$h.keys.elems} keys"; } describe(42); # 42 is an integer describe("hello"); # 'hello' is a string of 5 characters describe([1, 2, 3]); # An array with 3 elements describe({a => 1, b => 2});# A hash with 2 keys

Where Clauses

Add where constraints for even more specific dispatch:
multi sub classify(Int $n where * >; 0) { say "$n is positive" } multi sub classify(Int $n where * <; 0) { say "$n is negative" } multi sub classify(Int $n where 0) { say "zero" } classify(42); # 42 is positive classify(-7); # -7 is negative classify(0); # zero
multi sub fizzbuzz(Int $n where * %% 15) { "FizzBuzz" } multi sub fizzbuzz(Int $n where * %% 3) { "Fizz" } multi sub fizzbuzz(Int $n where * %% 5) { "Buzz" } multi sub fizzbuzz(Int $n) { ~$n } say (1..20).map(&fizzbuzz).join(", ");

Multi Methods

Multi dispatch works with methods too:
class Shape { multi method area(Shape:U:) { say "Call area on a specific shape instance"; } } class Circle is Shape { has $.radius; multi method area() { pi * $!radius ** 2 } } class Rectangle is Shape { has $.width; has $.height; multi method area() { $!width * $!height } } my @shapes = Circle.new(radius => 5), Rectangle.new(width => 3, height => 4); for @shapes -> $s { say "{$s.^name}: {$s.area}"; }

Dispatch Order

Raku tries the most specific candidate first. Specificity is determined by:
  1. Number of parameters
  2. Type constraints (more specific types win)
  3. Where clauses (add specificity)
multi sub process(Any $x) { say "Any: $x" } multi sub process(Numeric $x) { say "Numeric: $x" } multi sub process(Int $x) { say "Int: $x" } multi sub process(Int $x where * >; 100) { say "Big Int: $x" } process("hello"); # Any: hello process(3.14); # Numeric: 3.14 process(42); # Int: 42 process(200); # Big Int: 200

Pattern Matching with Multi

Multi dispatch gives you pattern matching for free:
# Recursive factorial multi sub fact(0) { 1 } multi sub fact(Int $n where * >; 0) { $n * fact($n - 1) } say fact(10); # 3628800 # Fibonacci multi sub fib(0) { 0 } multi sub fib(1) { 1 } multi sub fib(Int $n where * >; 1) { fib($n - 1) + fib($n - 2) } say fib(10); # 55
This reads like a mathematical definition.

Multi and Coercion

You can use multi dispatch to handle type coercion cleanly:
multi sub to-number(Str $s) { $s.Numeric } multi sub to-number(Numeric $n) { $n } multi sub to-number(Bool $b) { $b ?? 1 !! 0 } say to-number("42"); # 42 say to-number(3.14); # 3.14 say to-number(True); # 1

Proto Declarations

A proto declaration constrains all multi variants:
proto sub temperature(Str $scale, Numeric $degrees --> Str) {*} multi sub temperature('C', Numeric $c) { "{ $c * 9/5 + 32 }F" } multi sub temperature('F', Numeric $f) { "{ ($f - 32) * 5/9 }C" } multi sub temperature('K', Numeric $k) { "{ $k - 273.15 }C" } say temperature('C', 100); # 212F say temperature('F', 32); # 0C say temperature('K', 373.15);# 100C
The {*} in the proto body means "dispatch to the appropriate multi candidate."

Practical Example: Command Dispatcher

class CLI { multi method run('help') { say "Available commands: help, list, add, remove"; } multi method run('list') { say "Listing all items..."; } multi method run('add', Str $item) { say "Adding: $item"; } multi method run('remove', Str $item) { say "Removing: $item"; } multi method run(Str $cmd, *@args) { say "Unknown command: $cmd"; } } my $cli = CLI.new; $cli.run('help'); $cli.run('add', 'milk'); $cli.run('remove', 'bread'); $cli.run('frobnicate');

Nextsame and Nextwith

Inside a multi candidate, you can delegate to the next matching candidate:
multi sub log-it(Str $msg) { note "[LOG] $msg"; } multi sub log-it(Str $msg where *.contains('ERROR')) { note "[!!!] ALERT: error detected"; nextsame; # Call the less-specific Str candidate } log-it("all good"); # [LOG] all good log-it("ERROR: disk full"); # [!!!] ALERT: error detected\n[LOG] ERROR: disk full
Multi dispatch eliminates large if/elsif chains, makes code self-documenting, and lets Raku optimize dispatch for you. It is one of the features that makes Raku uniquely expressive.