This week on Ruby Bits we've decided to honour the Juno mission entering
Jupiter's orbit by discuss
another special operator in Ruby, commonly known as the spaceship operator
(because it looks like one). You've probably come across it at some point, but
if not, here's what I'm talking about: <=>
.
RubyConf Portugal 2016 discount code
If you want to meet us and/or see some talks on Ruby topics, come join us at RubyConf Portugal 2016. For being a Ruby Bits reader you get 25% off the price of the ticket.
History
Perl was likely the first language to introduce this operator as it is seen in Ruby, but the same concept exists in Java or C# as part of their Comparable interfaces.
How it works in Ruby
The spaceship operator in Ruby has four possible return values, -1
, 0
, 1
or nil
. It will return nil
only if the two objects are not comparable. If
they are comparable, it will return -1
if the caller object is considered less
than the argument (whatever that means for these objects), it returns 0
if
they are equal and 1
otherwise.
Sounds confusing? Let's look at an example that will make it clearer:
1 <=> 4 #=> -1
4 <=> 4 #=> 0
8 <=> 4 #=> 1
7 <=> "hello" #=> nil
Where to use it?
If you've never used this operator directly, you might be wondering why would
you care about it, or where should you use it. It turns out that <=>
is the
foundation to everything in the Comparable
module. When it is implemented for a class, it allows you to use the more common
comparison methods such as <
, >=
, between?
or even sort
.
As you can probably guess, Ruby already implements this for the core classes,
even for Object
which means your custom classes will also have a very basic
(and usually not very useful) version of the spaceship operator.
Making my class comparable
As I've stated above, all objects can be compared with objects of the same class
by default, that comparison might not be exactly what you want though. Let's
create a brand new Dummy
class and see how it behaves:
class Dummy
end
d1 = Dummy.new
d2 = Dummy.new
d1 <=> d2 #=> nil
d1 < d2 #=> undefined method `<'
Woah! What just happened? Well, when the spaceship operator returns nil
, none
of the Comparable
methods work, and the Object
's version of <=>
returns
nil
for every case, expect when the objects being compared are the exactly the same.
For instance, d1 <=> d1
would return 0
.
In order to make Dummy
actually comparable we need to really implement the
spaceship operator, like so:
class Dummy
include Comparable
def <=>(other)
0
end
end
d1 = Dummy.new
d2 = Dummy.new
d1 <=> d2 #=> 0
d1 < d2 #=> false
Notice that we included the Comparable
module and then implemented the <=>
method to always return 0
, which means all instances of this class are
considered equal. So that we can write a more interesting method, let's also add
a string attribute that we can compare:
class Dummy
include Comparable
attr_reader :name
def initialize(name)
@name = name
end
def <=>(other)
name <=> other.name
end
end
d1 = Dummy.new("hello")
d2 = Dummy.new("world")
d1 <=> d2 #=> -1
d1 < d2 #=> true
In this example we are simply delegating
the <=>
to String
, which in turn will declare that "hello"
is less than
"world"
because the unicode code point for h
is 104 and the one for w
is 119 and that's what's actually being compared.
More Ruby Bits
If you've enjoyed this Ruby Bit you should really subscribe to our newsletter, where other Ruby Bits and more great articles are shared every week.