Search code examples
rubyoperatorscomparison-operatorsspaceship-operator

What actually happens when Spaceship operator is overwritten in Ruby?


I am new to Ruby. Can someone kindly explain how the <=> method works in the following program. How is the 'other' parameter being used below?

class Age
  attr_reader :value

  def initialize(value)
  @value = value
  end

  def <=> (other) # What is actually happening here...
  self.value <=> other.value # ..and here?
  end

end

a = Age.new(7)
b = Age.new(3)
c = Age.new(9)

d = [a, b, c]
puts d.sort

Solution

  • Let's start with d.sort. d is an array, so you should read about Array#sort. The documentation says that sorting "will be done using the <=> operator". That means when sorting the array it will repeatedly evaluate x <=> y where x and y are different elements of d, in order to determine which elements are bigger/smaller.

    Operators in Ruby are a little tricky, because they're actually method calls in disguise. x <=> y is just a different way of writing x.<=>(y). That is, x has a method named <=> which is being passed y as an argument. Since the elements of d are instances of the Age class, Age needs to define the instance method <=>. So when you see def <=> (other), this is really no different from a normal method definition (def foo(other)) but with a different method name.

    Now that we're defining <=> for Age instances, what should the method actually do? Well each Age object stores a @value, so intuitively Age#<=> should compare the @values. That's what self.value <=> other.value does. This works because value returns a Fixnum, and luckily Fixnum#<=> is built into Ruby to do the proper comparison.

    For an example of what's going on behind the scenes, we can try sorting arrays containing values that we don't know how to compare:

    [Age.new(3), 4].sort
    # NoMethodError: undefined method `value' for 4:Fixnum
    [4, Age.new(3)].sort
    # ArgumentError: comparison of Fixnum with Age failed
    

    This shows how sorting different types leads to different <=> methods being called.