As I listened to the WWDC15 talk on Building Better Apps with Value Types in Swift I was struck by a sentence that I had never dawned on me before:

Every Value Type should be Equatable.

That is, every Value Type should conform to the `Equatable`

protocol.

Talk about a sweeping statement! Wow – *every* Value Type should be `Equatable`

? Hmm… Let’s unpack the “why’s” and “how’s” of this statement.

### Why?

I’d never thought about why I might want my Value Types in Swift to be Equatable. Not that I thought it’d be a terrible idea to implement the `==`

operator for a Type… I just never realized that this was actually *expected* behavior of Value Types!

The reasoning in the talk was that Values are intuitively *meant* to be compared for equality. *Because* they’re *Values*, there is inherent expectation from clients of the Type to be able to ask and know if one Value is equal to another Value of the same Type.

We naturally expect to be able to ask two variables/constants, each holding `Int`

Values (because in Swift, `Int`

is a Value Type), if they equal each other. And we naturally expect the comparison to compare the actual *numbers*… the *Values themlselves*.

1 2 3 4 5 6 7 |
let a = 10 let b = 5 + 2 + 3 a == b // true let x = 1 let y = 2 x == y // false |

Likewise, we naturally expect to ask two Strings if *they’re* equal:

1 2 3 4 5 6 7 |
let str1 = "I love Swift!" let str2 = "I love Swift!" str1 == str2 // true let str3 = "i love swift!" str1 == str3 // false - case-sensitive comparison |

In fact, we naturally expect to ask these kinds of equality questions about *any* of the Swift standard library Value Types, don’t we?

### How?

We *do* expect to test for equality between two Value Types. It just makes sense.

So now the question is, “*How*?”

The simple answer is that our Value Types need to implement an `==`

operator. But there’s something really important to consider:

#### Properties of equality

To be truly equal, the `==`

operator not only needs to be implemented, but it needs to be implemented in such a way that it *behaves* as we’d expect when doing our comparisons. During the talk, Doug mentioned three important properties of equality that need to hold for our Value Types:

- The comparison must be
**reflexive** - The comparison must be
**symmetric** - The comparison must be
**transitive**

That sounds awfully “math-y”. In fact, it’s the exact same terminology used in mathematics. But don’t worry, the terminology is simple and natural to understand.

##### Reflexive

To be reflexive, the Type’s `==`

operator needs to make sure that the expression `x == x`

returns `true`

.

So if I have `let x = 1`

and I write the expression `x == x`

, I do in fact get `true`

because `Int`

‘s equality operator is reflexive (as expected).

##### Symmetric

To be symmetric, the Type’s `==`

operator needs to compute things in such a way that the expression `x == y`

and `y == x`

return the same value.

Here’s an example of symmetry:

1 2 3 4 5 6 7 8 9 10 11 |
let x = 1 let y = 1 x == y // true y == x // true let str1 = "Hi" let str2 = "Hello" x == y // false y == x // false |

##### Transitive

Finally, to be transitive, the Type’s `==`

operator needs to compute things in such a way that when `x == y`

is `true`

, and `y == z`

is `true`

, then `x == z`

is also `true`

.

Here’s an example of transitivity:

1 2 3 4 5 6 7 |
let x = 100 let y = 50 + 50 let z = 50 * 2 x == y // true y == z // true x == z // true |

#### Implementation

*Most* of the time, the implementation of `==`

is very simple. If your Value Type is comprised of other Value Types that have an `==`

operator that’s correctly implemented with the semantics I just described, then the implementation for your Type is straight-forward.

An example might help to set things up for understanding. Suppose that we’re building a sight-seeing app for a local tourism company. We’ve got a struct called `Place`

to help us encapsulate the idea of… well… a “place” to visit. It looks something like this:

1 2 3 4 5 6 7 |
struct Place { let name: String let latitude: Double let longitude: Double // init is auto-generated by the compiler in this case } |

Since `Place`

is a Value Type (Struct) which is comprised of other Value Types, you’d simply need to do something like the following to make it `Equatable`

:

1 2 3 4 5 6 7 8 9 |
extension Place: Equatable {} func ==(lhs: Place, rhs: Place) -> Bool { let areEqual = lhs.name == rhs.name && lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude return areEqual } |

One of the first things to notice is that the `==`

operator has to be implemented as a stand-alone *global* function, rather than as part of the Type definition.

Notice also that even though we have the source for the Type that we want to make `Equatable`

, I chose to signal the `Equatable`

protocol adoption through an *extension* on the Type, rather than at the Type declaration itself. Both are acceptable, but it’s become convention to use the extension strategy for this particular protocol.

The implementation of `==`

uses the intuitive semantics that one `Place`

isn’t the same as another `Place`

unless the `name`

s, `latidude`

s, and `longitude`

s are all the same.

`lhs`

and `rhs`

simply mean “left-hand side” and “right-hand side”, respectively. Since there’s a `Place`

instance on the left-hand side of the `==`

operator, and a `Place`

instance on the right-hand side of the `==`

operator when we use it in practice, it makes sense to label these parameters according to that pattern.

The implementation could literally be read as, “If the `Place`

on the left-hand side’s `name`

is equal to the `Place`

on the right-hand side’s `name`

, AND … the `latitude`

… AND … the `longitude`

, then the two `Place`

instances are equal.”

##### Dealing with reference types

If Reference Types are involved with your Value Type implementation, things could get a little more complicated. “Complicated” probably isn’t the right word… but you do have to *think* a little more about your Type’s equality semantics.

Let’s modify the example just a little bit:

Supposing that `Place`

had an additional property called `featureImage`

which held a reference to a `UIImage`

instance (a Reference Type), we’d need to test for equality a little bit differently. And *how* we test for equality depends on the particulars of our Type’s equality semantics:

- Are the two
`Place`

s equal if both of them point to the same`featureImage`

(ie, should we just use`===`

to check and see if the references are the same)? - OR, are the two
`Place`

s equal if both of their`featureImage`

instances contain the same underlying bitmap (ie, they’re the same picture in*essence*)?

As you can see, the phrase “it depends” applies here. Certainly we need to test for *some* kind of equality on the `featureImage`

in order to have a complete `==`

implementation. But how we go about it really comes down to the semantics that you and others would expect from asking the question, “Is this `Place`

equivalent to that `Place`

?”

For this example, I’m going to go with the latter statement: that two `Places`

are equal if both of their `featureImage`

instances contain the same underlying bitmap.

1 2 3 4 5 6 7 8 9 10 |
extension Place: Equatable {} func ==(lhs: Place, rhs: Place) -> Bool { let areEqual = lhs.name == rhs.name && lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude && lhs.featureImage.isEqual(rhs.featureImage) // depends on your Type's equality semantics return areEqual } |

### Wrapping up

Every Value Type should conform to the `Equatable`

protocol. In this article, we unpacked the “why’s” and the “how’s” of this fundamental characteristic of Value Types. From here, we’ve all got to jump on board and ensure that we meet this expectation in our code!

Pingback: Swift: Equatable with Optionals()