Hyper Operators
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:
The pointy end of the operator indicates which side should be "extended" if the operands have different sizes.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
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:This is one of the most commonly used forms. It replacesmy @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)
.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 = , , , ; 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:Usemy @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
>> 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 }).; 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.