Fake Operators and Equality

In Ruby, most operators are actually methods:

1 + 3
# is actually...
1.+(3)

4 == 4
# is actually...
4.==(4)

This is true for the operators bellow:

Operators Description
`[]`, `[]=` Collection element getter/setter
`**` Exponential operator
`!`, `~`, `+`, `-` Not, complement, unary plus and minus
`*`, `/`, `%` Multiply, divide, and modulo
`+`, `-` Plus, minus
`>>`, `<<` Right and left shift
`&` Bitwise "and"
`^`, `|` Bitwise exclusive and inclusive "or"
`<=`, `<`, `>`, `>=` Less than/equal to, etc
`<=>`, `==`, `===`, `!=`, `=~`, `!~` Equality and pattern matching

Operators that are, in fact, methods, can be defined in custom classes1 to change their default behaviors. For example we could compare two Person classes by their age. To do that, we could use the greater than operator (>) between two instances:

class Person
  def initialize(age)
    @age = age
  end

  def >(other_person)
    age > other_person.age
  end

  def ==(other_person)
    age == other_person.age
  end

  protected

  attr_reader :age
end

arthur = Person.new(44)
tommy  = Person.new(43)

arthur > tommy  # => true
arthur == tommy # => false

(Note the use of protected which is perfect for this use case.)

Now, defining == method gives us !=. This is not true for > though.

arthur != tommy # => true
arthur < tommy  # => NoMethodError (undefined method `<' for...

If we know we are going to use a lot of comparison, we can include the Comparable module to our class and define <=>. By doing this, we are effectively defining all comparison methods.

class Person
  include Comparable

  def initialize(age)
    @age = age
  end

  def <=>(other_person)
    age <=> other_person.age
  end

  protected

  attr_reader :age
end

arthur = Person.new(44)
tommy  = Person.new(43)

arthur > tommy  # => true
arthur < tommy  # => false
arthur == tommy # => false

For the sake of completeness, let’s add that there are also operators which are not methods (and thus, can’t be defined):

Operators Description
`.`, `::` Method/constant resolution operators
`&&` Logical "and"
`||` Logical "or"
`..`, `...` Inclusive range, exclusive range
`?` `:` Ternary if-then-else
`=`, `%=`, `/=`, `-=`, `+=`, `|=`, `&=`, `>>=`, `<<=`, `*=`, `&&=`, `||=`, `**=`, `{` Assignment/shortcuts, block delimiter
  1. They can also be re-defined for default class. This is called Monkey Patching and is very dangerous, and thus, should be avoided for the time being.

    ↩︎