RubyBits: Triple Equals

By Luis FerreiraOn June 14, 2016

If you thought that the === operator was something only JavaScript developers had to deal with, you were mistaken. We also have one in Ruby, even though it differs immensely in terms of functionality, from its JS counterpart.

In Ruby, the === is referred to as Case Equality, undoubtedly because it's most common use is hidden away behind the case statement. That means that if you've ever used a case statement in Ruby you have been using our friend triple equals all along.

Using Case Equality

As mentioned the most common use of this operator is in the case statement, for example:

case num
when 1 then "one"
when 2 then "two"
else "nothing"

Behind the scenes, Ruby is actually calling 1 === num and 2 === num. You can try it yourself, go to irb and type 1 === 1, it should be true.

Case Equality on other types

Now to make it a little harder, do this (1..10) === 5. After you've pressed enter you should have irb saying it is true, which is both great and makes sense, since 5 is clearly in the range between 1 and 10.

Make no mistake, there is not magic here, === is an operator defined by Object to do the same as ==, but other classes can overwrite it to do whatever they please, that's exactly what Range does.

Many other basic ruby types (or classes) do it, such as Regexp for regular expressions, /sub/ === "subvisual" should be true. Bare in mind thought that it is dependent on the order of the arguments, if you tried "subvisual" === /sub/ it would be false, since the === for String will do "subvisual".eql?(/sub/) because the regex is not a String and does not respond to to_str (see the docs here).

Another way you can use the case equality is with the Module class (of which Class inherits from), since it will behave similarly to is_a?, allowing to do Integer === 1 and have it be true.

Lambdas and Procs

Lambdas and Procs behave in an even more interesting way when combined with the case equality, by taking advantage of the fact that any Proc can be run with call method and calling it implicitly with the object being compared. For instance, { |num| num + 2 } === 3 is equal to { |num| num + 2 }.call(3) and both will evaluate to 5.

This can be used in a case statement to make it read a little bit better. Assuming we want to do things based on a number being even or odd, we could do this:

def even?
  -> (num) { num.even? }

def odd? { |num| num.odd? }

case a
when even? then "even"
when odd? then "odd"
else "impossible"

Notice that I use both a lambda (using the stabby lambda syntax) and a Proc since they behave in very similar ways.

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.

If you want to meet us and/or see some talks on Ruby topics, come join us at RubyConf Portugal 2016. For making all the way to the end of this article you get 25% off the price of the ticket.