perl.gg / secret-operators

Spaceship Operator

2026-01-17

This page explains how to sort lists in Perl using the spaceship operator. You will reach for it whenever you need to put a collection in order. The operator is just three characters:
<=>
The spaceship operator. Three characters that tell you how two numbers relate to each other.
5 <=> 10 # -1 (less than) 10 <=> 5 # 1 (greater than) 7 <=> 7 # 0 (equal)
This is a three-way comparison. It returns -1, 0, or 1, which is exactly what sort expects.

Part 1: THE BASICS

The spaceship operator compares two numbers and returns:
-1 if left is less than right 0 if they're equal 1 if left is greater than right
That output matches what sort expects. You pass a block to sort that uses $a and $b, and Perl handles the rest.
my @nums = (5, 2, 9, 1, 7); my @sorted = sort { $a <=> $b } @nums; # (1, 2, 5, 7, 9)
If you skip the operator, Perl falls back to string sorting. Numbers then compare by their first character instead of their value.
my @wrong = sort @nums; # (1, 2, 5, 7, 9) - looks right... my @nums = (5, 2, 19, 1, 7); my @wrong = sort @nums; # (1, 19, 2, 5, 7) - WRONG! String "19" < "2"

Part 2: ASCENDING VS DESCENDING

You reverse the order by swapping $a and $b in the sort block.
# Ascending (smallest first) my @asc = sort { $a <=> $b } @nums; # Descending (largest first) my @desc = sort { $b <=> $a } @nums;
The trick is remembering which is which. You can think of it like subtraction.
$a <=> $b # $a - $b: positive means a is bigger, goes later $b <=> $a # $b - $a: positive means b is bigger, b goes later .--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/

Part 3: THE STRING VERSION

The spaceship operator handles numbers. For strings, Perl provides cmp:
'apple' cmp 'banana' # -1 (a before b) 'banana' cmp 'apple' # 1 (b after a) 'apple' cmp 'apple' # 0 (equal)
It returns the same three values, but it compares text instead of numbers.
my @words = qw(cherry apple banana); my @sorted = sort { $a cmp $b } @words; # (apple, banana, cherry)
You can also skip the block for simple string sorting.
my @sorted = sort @words; # Same result
Perl's default sort uses string-based cmp automatically.

Part 4: MULTI-LEVEL SORTING

The real power shows up in complex sorts. You chain comparisons with ||:
my @people = ( { name => 'Alice', age => 30 }, { name => 'Bob', age => 25 }, { name => 'Carol', age => 30 }, ); my @sorted = sort { $a->{age} <=> $b->{age} # First: by age || $a->{name} cmp $b->{name} # Then: by name } @people;
Result:
Bob (25), Alice (30), Carol (30)
The || operator short-circuits. If the age comparison returns 0, Perl falls through to the name comparison.

Part 5: THREE-LEVEL AND BEYOND

You keep chaining for more levels.
my @sorted = sort { $a->{department} cmp $b->{department} || $b->{salary} <=> $a->{salary} # Descending salary || $a->{hire_date} cmp $b->{hire_date} || $a->{name} cmp $b->{name} } @employees;
Each level only matters when the previous levels match.

Part 6: SORTING HASH KEYS BY VALUE

This is a classic pattern for sorting keys by their corresponding values.
my %scores = ( alice => 85, bob => 92, carol => 78, ); # Sort names by score (highest first) my @ranking = sort { $scores{$b} <=> $scores{$a} } keys %scores; # (bob, alice, carol)
The sort block compares values, but you are sorting the keys.

Part 7: CUSTOM COMPARISONS

The spaceship operator works with any numeric expression.
# Sort by string length my @by_length = sort { length($a) <=> length($b) } @words; # Sort by last character my @by_last = sort { ord(substr($a,-1)) <=> ord(substr($b,-1)) } @words; # Sort IPs numerically my @ips = sort { my @a = split /\./, $a; my @b = split /\./, $b; $a[0] <=> $b[0] || $a[1] <=> $b[1] || $a[2] <=> $b[2] || $a[3] <=> $b[3] } @ip_addresses;

Part 8: THE SCHWARTZIAN TRANSFORM

You use this pattern when sorting requires expensive computation.
# Slow: computes length every comparison my @sorted = sort { length($a) <=> length($b) } @strings; # Fast: compute once, sort, extract my @sorted = map { $_->[0] } # 3. Extract original sort { $a->[1] <=> $b->[1] } # 2. Sort by cached value map { [ $_, length($_) ] } # 1. Cache expensive calc @strings;
The transform is named after Randal Schwartz. The spaceship operator handles the middle step.

Part 9: GOTCHAS

You will see warnings if your list contains undefined values.
my @nums = (5, undef, 3, undef, 1); my @sorted = sort { $a <=> $b } @nums; # Warnings! undef used in numeric comparison
You can fix that with defined-or checks.
my @sorted = sort { ($a // 0) <=> ($b // 0) } @nums;
Non-numeric strings also cause trouble.
my @mixed = ('5', 'ten', '3'); my @sorted = sort { $a <=> $b } @mixed; # 'ten' becomes 0 numerically, with warnings
The spaceship operator is for numbers. Know your data.

Part 10: VS OTHER LANGUAGES

Many languages borrowed the operator from Perl.
Ruby: 5 <=> 10 PHP: 5 <=> 10 (as of PHP 7) Groovy: 5 <=> 10
Python uses cmp() functions differently.

Perl shipped it first. Version 5 arrived in 1994.

Part 11: THE NAME

Look at the characters again.
<=>
It looks like a spaceship viewed from above. The < and > are wings, and the = is the body.
<=> / \ < = > \ /
You might need to lean into the shape a bit. The name stuck anyway, and every language that copied it uses the same label.
<=> / \ < = > \ / / \ / \ Three-way comparison since 1994
perl.gg