128

Possible Duplicate:
=== vs. == in Ruby

I've seen it used a few times lately but can't figure out what it does. Can anyone illustrate how it works?

Community
  • 1
  • 1
stephenmurdoch
  • 34,024
  • 29
  • 114
  • 189
  • 14
    @Gabe: `===` has absolutely nothing whatsoever to do with equality. In particular, it violates pretty much every law that you would ecpect an equality operator to follow. And it does that very much by design. – Jörg W Mittag Dec 17 '10 at 05:08
  • So, it's more like "could be equal, sorta"? – Gabe Dec 17 '10 at 05:28
  • 5
    No, it's not like "could be equal, sorta". The comparison of the left and right sides of `===` changes, depending on what types the objects are; It's a context-sensitive comparison. You need to study and experiment with `case/when` or `===` in IRB to see it in action. `1===1` compares two Fixnum for equal values. `(1..3)===1` looks in the range to see if `1` is included. It's much more than a simple equality test and because of the context-sensitive nature of it you can't necessarily substitute `===` and `==`. – the Tin Man Dec 17 '10 at 15:51

3 Answers3

315

Just like with every other method in Ruby (or actually pretty much any object-oriented language),

a === b

means whatever the author of a's class wants it to mean.

However, if you don't want to confuse the heck out of your colleagues, the convention is that === is the case subsumption operator. Basically, it's a boolean operator which asks the question "If I have a drawer labelled a would it make sense to put b in that drawer?"

An alternative formulation is "If a described a set, would b be a member of that set?"

For example:

 (1..5) === 3           # => true
 (1..5) === 6           # => false

Integer === 42          # => true
Integer === 'fourtytwo' # => false

  /ell/ === 'Hello'     # => true
  /ell/ === 'Foobar'    # => false

The main usage for the === operator is in case expressions, since

case foo
when bar
  baz
when quux
  flurb
else
  blarf
end

gets translated to something (roughly) like

_temp = foo

if bar === _temp
  baz
elsif quux === _temp
  flurb
else
  blarf
end

Note that if you want to search for this operator, it is usually called the triple equals operator or threequals operator or case equality operator. I really dislike those names, because this operator has absolutely nothing whatsoever to do with equality.

In particular, one would expect equality to be symmetric: if a is equal to b, then b better be also equal to a. Also, one would expect equality to be transitive: if a == b and b == c, then a == c. While there is no way to actually guarantee that in a single-dispatch language like Ruby, you should at least make an effort to preserve this property (for example, by following the coerce protocol).

However, for === there is no expectation of either symmetry or transitivity. In fact, it is very much by design not symmetric. That's why I don't like calling it anything that even remotely resembles equality. It's also why I think, it should have been called something else like ~~~ or whatever.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • 19
    I like your comparison with Sets, it helps make the most sense of this "operator". – coder_tim Dec 17 '10 at 17:07
  • 3
    Wow! You got to have the best understanding of the ***subsumption*** operator, given that the best answers concerning this matter are coming from you. It was really confusing me, so thanks :) – Maher4Ever Aug 06 '11 at 21:45
  • 4
    Example of asymmetry: `Symbol === :symbol # => true` `:symbol === Symbol # => false` – tbloncar Apr 13 '14 at 15:39
  • 2
    What is case supsumption techincal definition? I don't come across this term much. Do you mean identity operator? – JohnMerlino Oct 20 '14 at 01:49
  • 1
    @JohnMerlino I think OP gives a pretty good definition of "case subsumption" right after he first mentions it: "Basically, it's a boolean operator which asks the question 'If I have a drawer labelled `a` would it make sense to put `b` in that drawer?'" – Jazz Mar 11 '15 at 16:15
  • 3
    BTW, `===` isn't reflexive either : `String === String # => false` so it doesn't have any of the [3 basic properties required for equalities](https://en.wikipedia.org/wiki/Reflexive_relation). – Eric Duminil Apr 03 '17 at 14:15
  • And by the way, specifically with the class `Set`, it's just not working like that by default. It should be implemented like `class Set; def === x; (include? x) or (self == x); end; end` – Berci Oct 18 '17 at 09:07
  • It's defined as an alias for `include?` specifically: https://github.com/ruby/ruby/blob/d2aaf865e3e5ecfcd10c879ce4263dc3a8f7fccf/lib/set.rb#L569 – Asherah Jan 11 '21 at 03:00
  • "Subsume" means to include or absorb, for those wondering. – Joel Dec 30 '22 at 03:26
6

Thanks for your edit Jacob, I was about to call you out ;) I'll post a couple of examples anyway. The implementation of === differs depending on type. For example:

(1...3) === 2
=> true

/test/ === "this is a test"
=> true

case 'test'
when /blah/
  "Blach"
when /test/
  "Test"
else
  "Fail"
end
=> "Test"

Stephen, checkout http://ruby-doc.org/docs/ProgrammingRuby/ (the "Pickaxe"), it should be able to help you out with questions such as this in the future.

Bauerpauer
  • 930
  • 6
  • 3
  • Cool stuff. But, still they're not "real" operators. In your example, say, `Range` has an `===` *method*. – Jacob Relkin Dec 17 '10 at 04:14
  • 2
    @Jacob Relkin, sure it's a real operator, it just isn't a *consistent* operator like `==` because what is being tested can change depending on the objects. [See the answer](http://stackoverflow.com/questions/3422223/vs-in-ruby/3422349#3422349) @Jörg W Mittag gave in a previous question. – the Tin Man Dec 17 '10 at 15:53
  • I think @JacobRelkin meant that it's not a "real operator" in the sense that it's actually a method -- rather than a language construct, which is what most programmers expect when they hear the term "operator." – Jazz Mar 11 '15 at 16:59
  • 1
    `==` is also "actually a method" and you can change it's behavior in any class you wish to. That doesn't mean it's not an operator ;) – nzifnab Mar 16 '16 at 20:43
2

In Ruby, the === operator is used to test equality within a when clause of a case statement. In other languages, the above is true.

To my knowledge, Ruby doesn't have true operators, they are all methods which are invoked on the LHS of the expression, passing in the RHS of the expression. So, really, you could override any "operator" you want in your classes to perform whatever the heck you want (analogous to operator overloading in C++).

Jacob Relkin
  • 161,348
  • 33
  • 346
  • 320
  • See [case expressions](http://phrogz.net/ProgrammingRuby/frameset.html?content=http%3A//phrogz.net/ProgrammingRuby/tut_expressions.html%23caseexpressions) from the free version of the Pickaxe book. – Phrogz Dec 17 '10 at 04:08
  • 3
    It doesn't test "equality" in any sense. It tests set membership. – Thomas Andrews Sep 23 '15 at 12:16
  • 2
    It does have operators, the assignment **operator** is not a method: It can't be redefined. – YoTengoUnLCD Jul 15 '16 at 18:16
  • 1
    @YoTengoUnLCD sure you can redefine the assignment operator. That's what setters do. – flash Sep 11 '19 at 21:56