raku.gg / functional

Hyper Operators

2026-04-04

Hyper operators are Raku's way of applying operations element-wise across data structures. They let you express "do this to every element" without writing explicit loops, and they signal to the runtime that operations can potentially be parallelized.

The >> and << Operators

The >> and << (or their Unicode equivalents >> and <<) distribute an operation across list elements:
my @numbers = 1, 2, 3, 4, 5; say @numbers >;>+>> 10; # (11 12 13 14 15) say @numbers >;>*>> 2; # (2 4 6 8 10) say @numbers >;>**>> 2; # (1 4 9 16 25) # Unicode version say @numbers >;>+>> 10; # Same result
The pointy end of the operator indicates which side should be "extended" if the operands have different sizes.

Direction Matters

The chevrons indicate how mismatched sizes should be handled:
my @a = 1, 2, 3, 4; my @b = 10, 20; # >> on the right: extend the right side to match the left say @a >;>+>> @b; # (11 22 13 24) -- @b cycles: 10,20,10,20 # << on the left: extend the left side to match the right say @b <;<+<< @a; # (11 22 13 24) # Both point outward (>>op<<): both sides must be same length # say @a >>+<< @b; # Dies: lists must be same length # Both point inward (<<op>>): shorter side is extended say @a <;<+>> @b; # (11 22 13 24) -- flexible matching

Method Calls with >>

Apply a method to every element:
my @words = <;hello world foo bar>;; say @words>;>.uc; # (HELLO WORLD FOO BAR) say @words>;>.chars; # (5 5 3 3) say @words>;>.flip; # (olleh dlrow oof rab) say @words>;>.substr(0,2); # (he wo fo ba)
This is one of the most commonly used forms. It replaces .map(*.method) in many cases.

Chaining Hyper Methods

You can chain multiple >> method calls:
my @data = <; Hello World Raku >;; say @data>;>.trim>;>.lc>;>.chars; # (5 5 4)

Hyper on Nested Structures

>> descends into nested structures:
my @nested = [1, 2], [3, 4], [5, 6]; say @nested>;>.map(*+10); # ((11 12) (13 14) (15 16))

Hyper with Unary Operators

Prefix operators work too:
my @bools = True, False, True, False; say @bools>;>.not; # (False True False True) my @nums = 1, -2, 3, -4; say @nums>;>.abs; # (1 2 3 4)

Arithmetic Examples

my @prices = 10.00, 25.50, 8.99, 42.00; my @tax-rate = 0.13; # 13% tax # Calculate tax for each price my @taxes = @prices >;>*>> @tax-rate; say @taxes; # (1.3 3.315 1.1687 5.46) # Calculate totals my @totals = @prices >;>+>> @taxes; say @totals; # (11.3 28.815 10.1587 47.46) # Percentage of total my $grand = @totals.sum; say @totals >;>/>> $grand >;>*>> 100; # Percentage of each item

Comparison Operators

Hyper operators work with comparisons too, returning lists of boolean values:
my @scores = 85, 92, 67, 78, 95; my $passing = 70; say @scores >;>>=>> $passing; # (True True False True True) say @scores.grep(* >;= $passing); # (85 92 78 95) -- for filtering, grep is better

String Operations

my @names = <;alice bob carol>;; my @domains = <;gmail.com yahoo.com outlook.com>;; # Build email addresses say @names >;>~>> '@' >;>~>> @domains; # (alice@gmail.com bob@yahoo.com carol@outlook.com)

Hyper vs Map

Both achieve similar results, but they communicate different intent:
my @data = 1..10; # These produce the same result: say @data>;>.is-prime; # Hyper -- "apply to each" say @data.map(*.is-prime); # Map -- "transform each" # Hyper can potentially parallelize; map processes sequentially # Hyper is more concise for simple method calls # Map is more flexible for complex transformations
Use >> for simple method calls and element-wise operations. Use .map when you need a block with logic.

Hyper Assignment

You can use hyper operators with assignment:
my @values = 1, 2, 3, 4, 5; @values >;>+=>> 10; say @values; # [11 12 13 14 15] @values >;>*=>> 2; say @values; # [22 24 26 28 30]

Working with Hashes

Hyper operators work on hash values too:
my %scores = math => 85, science => 92, english => 78; # Apply a curve to all scores my %curved = %scores.map({ .key => .value + 5 }).Hash; say %curved; # {english => 83, math => 90, science => 97}

Practical Example: Data Normalization

my @raw-data = 23, 45, 12, 67, 34, 89, 56; # Min-max normalization to 0..1 range my $min = @raw-data.min; my $max = @raw-data.max; my @normalized = (@raw-data >;>->> $min) >;>/>> ($max - $min); say @normalized.map(*.round(0.01)); # (0.14 0.43 0 0.71 0.29 1 0.57)

Practical Example: Vector Math

my @v1 = 1, 2, 3; my @v2 = 4, 5, 6; # Vector addition say @v1 >;>+<< @v2; # (5 7 9) # Dot product say [+] @v1 >;>*<< @v2; # 32 # Scalar multiplication say @v1 >;>*>> 3; # (3 6 9) # Vector magnitude say sqrt [+] @v1 >;>**>> 2; # 3.7416573867739413

Performance Note

While hyper operators theoretically allow parallelization, in practice Rakudo (the Raku compiler) may or may not parallelize them depending on the operation and data size. For guaranteed parallelism, use .hyper or .race (covered in a separate post). The value of >> is primarily in expressiveness and readability.

Hyper operators make element-wise operations concise and readable. They are one of those features that feel natural once you start using them, and they make Raku code unmistakably expressive.