# Loose Equality (was equal? versus eql? versus == versus === verus \<=\> )

**URL:** <https://www.ruby-forum.com/t/loose-equality-was-equal-versus-eql-versus-versus-verus/121469>\
**Category:** Ruby\
**Created:** [November 28, 2007, 7:07am UTC](https://www.ruby-forum.com/t/loose-equality-was-equal-versus-eql-versus-versus-verus/121469 "2007-11-28T07:07:19Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![pfharlock](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/f04885/32.png) [@pfharlock](https://www.ruby-forum.com/u/pfharlock)\
**Post date:** [November 28, 2007, 7:07am UTC](https://www.ruby-forum.com/t/loose-equality-was-equal-versus-eql-versus-versus-verus/121469/1 "2007-11-28T07:07:19Z")

</div>

> > hash keys by key impractical.
> 
> foo.eql? still seems a little inconsistent (and surprising) to me.  
> Essentially, it’s == except when comparing hashes, at which point it  
> suddenly becomes foo.equal?.

This isn’t really true. I just spent some time looking at the 1.8  
sources  
for Object/Array/String/Hash/Set in order to understand things better  
and  
found some interesting insights into eql? vs == that I haven’t seen  
mentioned before.

Here is how I would characterize equal?, eql? and ==

equal? object identity test  
eql? strict equality test (defaults to identity)  
== loose equality test (defaults to identity)

Strict equality for Object, Array, String, and Hash means that  
the class of the objects are identical. I mention this because that  
is not the case with loose equality.

Array and String define ‘strict equality’ based on their contents. The  
containers don’t have have to be identical but the contents must follow  
the ‘strict equality’ rule. For arrays, this means considering x.eql?  
(y)  
for all the corresponding elements. For strings, it just means a byte  
comparison of the contents.

Object and Hash define ‘strict equality’ as ‘object identity’. The  
other messages in the thread give some reasons why this is a reasonable  
definition for Hash (i.e. why the contents are ignored).

The more interesting concept is ‘loose equality’ defined by ==.  
What I didn’t know and what isn’t clear from the documentation is  
that Array, String, and Hash all implement a coercion ‘protocol’ for  
#==. If the object on the right hand side responds to to\_ary, to\_str,  
or to\_hash respectively, then the receiver and argument are reversed and  
#== is called again.

```
  str == str_pretender

```

becomes

```
  str_pretender == str

```

at which point the implementation of == as provided by the ‘pretender’  
comes into play. This little trick means that can you construct  
your own classes that pretend to be arrays, strings, or hashes and  
== will commute correctly with the standard classes as long as you  
take care to define to\_ary/to\_str/to\_hash and ==. Nice.

In addition, it is important to note that while Hash#== uses #eql?  
to compare _keys_ it uses #== to compare values:

```
 h1 = {1 => 2}
 h2 = {1 => 2.0}
 h3 = {1.0 => 2.0}

 h1.eql?(h2) # false (identity test)
 h1 == h2 # true (2 == 2.0 is true)
 h1 == h3 # false (1.eql?(1.0) is false)

```

Because #== is used on the values, the coercion protocol I mentioned  
above will come into play when comparing hash contents.

I didn’t look at \<=\> as closely but it looks like Array#\<=\> will  
happily convert the right hand side via #to\_ary if available.  
String#\<=\> looks for to\_str and then applies the coercion protocol  
as with == taking care to reverse the results.  
Objects and Hashes don’t have a natural ordering so \<=\> isn’t defined.

Set

Set is a bit strange. Set#eql? depends on Hash#eql? on an internal  
hash but it isn’t possible for two sets to share the same internal  
hash and since Hash#eql? is based on object identity it isn’t possible  
for Set#eql? to ever return true, which means that ‘strict equality’  
for sets is the same as object identity. If the definition of Hash#eql?  
changed, then Set#eql? would follow. Set#== doesn’t implement a  
coercion protocol and sets aren’t ordered so no Set#\<=\>.

Gary W.
