raku.gg / metaprogramming

Custom Operators

2026-04-08

Raku lets you define your own operators with custom symbols, precedence, and associativity. This is not just syntactic sugar; it is a powerful metaprogramming feature that lets you create domain-specific notation that reads naturally.

Defining Infix Operators

An infix operator sits between two operands:
sub infix:<plus>;($a, $b) { $a + $b } say 3 plus 5; # 8
You can use any string as an operator name:
sub infix:<avg>;($a, $b) { ($a + $b) / 2 } say 10 avg 20; # 15

Unicode Operators

Raku supports Unicode in operator names, which can make mathematical code beautiful:
sub infix:<+++>($a, $b) { $a + $b + 1 } say 3 +++ 5; # 9 # Cross product for 2D vectors sub infix:<cross>;(@a, @b) { @a[0] * @b[1] - @a[1] * @b[0] } say [3, 4] cross [1, 2]; # 2

Prefix and Postfix Operators

Prefix operators come before their operand:
sub prefix:<not>;($x) { !$x } say not True; # False say not False; # True sub prefix:<double>;($x) { $x * 2 } say double 21; # 42
Postfix operators come after:
sub postfix:<!>($n) { [*] 1..$n } say 5!; # 120 say 10!; # 3628800

Circumfix Operators

Circumfix operators surround their operand:
sub circumfix:<[ ]>(*@items) { @items.sort.list } say [3, 1, 4, 1, 5]; # This is array syntax, would need different symbols sub circumfix:<{{ }}>(*@items) { @items.sort.list } say {{3, 1, 4, 1, 5}}; # (1 1 3 4 5) -- sorted

Postcircumfix Operators

These are operators like array indexing:
class Matrix { has @.data; has $.rows; has $.cols; } sub postcircumfix:<[ ]>(Matrix $m, @index) { $m.data[@index[0] * $m.cols + @index[1]] }

Setting Precedence

Use is tighter, is looser, or is equiv to control precedence:
# Same precedence as multiplication sub infix:<dot>;(@a, @b) is equiv(&infix:<*>) { [+] @a Z* @b } say [1,2,3] dot [4,5,6]; # 32 # Tighter than addition sub infix:<choose>;($n, $k) is tighter(&infix:<+>) { ([*] ($n - $k + 1)..$n) / ([*] 1..$k) } say 10 choose 3; # 120 say 10 choose 3 + 1; # 121 (choose binds tighter, so: (10 choose 3) + 1)

Setting Associativity

Control how operators chain:
# Left-associative (default): a op b op c = (a op b) op c sub infix:<then>;($a, $b) is assoc<;left>; { "$a -> $b" } say "a" then "b" then "c"; # a -> b -> c # Right-associative: a op b op c = a op (b op c) sub infix:<arrow>;($a, $b) is assoc<;right>; { "$a -> $b" } say "a" arrow "b" arrow "c"; # a -> b -> c # Non-associative: a op b op c is a compile error sub infix:<cmp-to>;($a, $b) is assoc<;non>; { $a <;=> $b } # say 1 cmp-to 2 cmp-to 3; # Compile error!

Practical Example: Unit Conversions

sub postfix:<kg>;($n) { $n } sub postfix:<lb>;($n) { $n * 0.453592 } sub postfix:<oz>;($n) { $n * 0.0283495 } sub postfix:<km>;($n) { $n } sub postfix:<mi>;($n) { $n * 1.60934 } sub postfix:<ft>;($n) { $n * 0.0003048 } say 150lb; # 68.0388 (in kg) say 100mi; # 160.934 (in km) say 6ft; # 0.0018288 (in km) # Comparison works naturally say 10km >; 6mi; # True (10 > 9.656)

Practical Example: String Operators

# Repeat with a visual operator sub infix:<x*>($str, $n) { $str x $n } say "ha" x* 3; # hahaha # String contains sub infix:<contains>;($haystack, $needle) { $haystack.contains($needle) } say "hello world" contains "world"; # True # String between sub infix:<between>;(Str $str, @bounds) { $str gt @bounds[0] &;& $str lt @bounds[1] } say "dog" between <;cat fish>;; # True (alphabetically between)

Practical Example: Pipe Operator

Create a Unix-style pipe for function composition:
sub infix:<|>>($data, &;func) is assoc<;left>; { func($data) } my $result = " Hello, World! " |> *.trim |> *.lc |> *.words |> *.elems; say $result; # 2

Practical Example: Null-Coalescing

sub infix:<???>($a, $b) { $a.defined ?? $a !! $b } my $name = Nil ??? "Anonymous"; say $name; # Anonymous my $port = 8080 ??? 3000; say $port; # 8080

Overloading Existing Operators

You can add multi candidates to existing operators for new types:
class Vector { has @.components; method Str() { "<{@!components.join(', ')}>" } } multi sub infix:<+>(Vector $a, Vector $b --> Vector) { Vector.new(components => ($a.components Z+ $b.components).list) } multi sub infix:<*>(Vector $v, Numeric $n --> Vector) { Vector.new(components => $v.components.map(* * $n).list) } multi sub prefix:<->(Vector $v --> Vector) { Vector.new(components => $v.components.map(-*).list) } my $v1 = Vector.new(components => [1, 2, 3]); my $v2 = Vector.new(components => [4, 5, 6]); say $v1 + $v2; # <5, 7, 9> say $v1 * 3; # <3, 6, 9> say -$v1; # <-1, -2, -3>

Guidelines

Custom operators show Raku's philosophy of giving the programmer maximum expressiveness. When used well, they can make domain-specific code read almost like the notation experts in that domain already use.