Skip to content

Commit

Permalink
Add 55: "Struggling Four Equality"
Browse files Browse the repository at this point in the history
  • Loading branch information
janlelis committed May 24, 2016
1 parent 014f9eb commit 3fcc648
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 0 deletions.
1 change: 1 addition & 0 deletions source/categories/index.html.erb
Expand Up @@ -80,6 +80,7 @@ title: Idiosyncratic Ruby - Browse by Category
<li><a href="/50-naming-too-good.html">Identifiers to Avoid</a>/<li>
<li><a href="/52-constant-visibility.html">Private & Derecated Constants</a></li>
<li><a href="/54-try-converting.html">Class <code>.try_convert</code></a></li>
<li><a href="/55-struggling-four-equality.html"><code>.equal?</code>, <code>eql?</code>, <code>==</code>, <code>===</code></a></li>
</ul>

<h2>Miscellaneous</h2>
Expand Down
69 changes: 69 additions & 0 deletions source/posts/55-struggling-four-equality.html.md
@@ -0,0 +1,69 @@
---
title: Struggling Four Equality
date: 2016-05-24
tags: core, equal, types
---

Another of Ruby's idiosycrasies is equalness. It's not too complicated, but naming is an issue here.

ARTICLE

## Four Concepts of Equalness

### `equal?` Object Identity Comparison

This one is easy. Two objects should be considered identical. Think: `x.object_id == y.object_id`

### `==` Equality Equality

This is the usual method to care about. Two objects should be treated the same.

### `eql?` Hash Key Equality

Normally, this is the same as `==`: ["…`eql?` is usually aliased to the overridden `==` method"](http://ruby-doc.org/core-2.3.1/Hash.html#class-Hash-label-Hash+Keys)

The most important effect of the result of `eql?` is to distinguish between hash keys: ["Two objects refer to the same hash key when their `hash` value is identical and the two objects are `eql?` to each other"](http://ruby-doc.org/core-2.3.1/Hash.html#class-Hash-label-Hash+Keys). A real life example:

1 == 1.0 # => true
1.eql?(1.0) # => false

# this means that the following will be treated as two different keys
{1: "Idiosyncratic", 1.0 "Hash"}

So `eql?` is a little stricter than `==`, because it will return `false` if two objects are not instances of the same class. A typical implementation looks like this:

def eql?(other)
instance_of?(other.class) && self == other
end

### `===` Fancy Equality

Implicitely used for `case` statements. Usually like `==`, but can also mean that something has some kind of relationship, like being [some kind of a class](https://github.com/janlelis/sig/blob/v1.0.1/lib/sig.rb#L108-L129).

## Equality Implementations for Core Classes

Class | `eql?` | `==` | `===`
------------------|--------|------|------
Object | Identity (like `equal?`) | Same as `eql?` | Same as `==`
Symbol | - | - | -
Numeric | Same type, same value | Same value, according to [spaceship returning `0`](http://ruby-doc.org/core-2.3.1/Numeric.html#method-i-3C-3D-3E) | -
String | Same length, same contents | If other is a String: `eql?`, else: `other.to_str === self` | -
Regexp | If other is a Regexp: Same pattern, same options, same encoding | Same as `eql?` | If other is a String: Match against self
Array | Same length, every element `.eql?` corresponding other element | Same length, every element `==` corresponding other element. Will [implicetly convert](/54-try-converting.html) other object via `.to_ary` | -
Hash | Same length, every element `==` corresponding other element (order not relevant) | Same as `eql?` | -
Module | - | - | `other.is_a?(self)`
Class | - | - | `other.is_a?(self)`
{:.table-13-29-29-X}

Meaning of `-`: Not defined / Use `Object`'s implementation

## Best Practices for Sub Classes

- Define a meaningful `==` which returns `true` if two objects should be considered the same
- Make `eql?` return the same value `==`, but also limit it to return only `true` if both object are instances of the same class
- Don't redefine `equal?`
- Be creative with `===` (On a reasonable level. Other people using your code expect it to be some kind of useful relationship check)

## Also See

- [equalizer: Automatically define `==` and `eql?` based on the object's instance variables](https://github.com/dkubb/equalizer)
6 changes: 6 additions & 0 deletions source/stylesheets/table-width.scss
Expand Up @@ -119,3 +119,9 @@
th:nth-child(3) { width: 15%; }
th:nth-child(4) { width: 25%; }
}

.table-13-29-29-X {
th:nth-child(1) { width: 13%; }
th:nth-child(2) { width: 29%; }
th:nth-child(3) { width: 29%; }
}

1 comment on commit 3fcc648

@gogiel
Copy link

@gogiel gogiel commented on 3fcc648 May 25, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.