| 1 | # Equality |
| 2 | |
| 3 | ## Structural Equality by Default |
| 4 | |
| 5 | In v3, `Equal.equals` used **reference equality** for plain objects and arrays. |
| 6 | Structural comparison was only available inside a `structuralRegion`, which |
| 7 | temporarily enabled deep comparison. Outside a structural region, two distinct |
| 8 | objects with identical contents were not considered equal: |
| 9 | |
| 10 | ```ts |
| 11 | // v3 |
| 12 | import { Equal } from "effect" |
| 13 | |
| 14 | Equal.equals({ a: 1 }, { a: 1 }) // false — reference equality |
| 15 | Equal.equals([1, 2], [1, 2]) // false — reference equality |
| 16 | ``` |
| 17 | |
| 18 | In v4, `Equal.equals` uses **structural equality** by default. Plain objects, |
| 19 | arrays, `Map`s, `Set`s, `Date`s, and `RegExp`s are compared by value without |
| 20 | opting in: |
| 21 | |
| 22 | ```ts |
| 23 | // v4 |
| 24 | import { Equal } from "effect" |
| 25 | |
| 26 | Equal.equals({ a: 1 }, { a: 1 }) // true |
| 27 | Equal.equals([1, [2, 3]], [1, [2, 3]]) // true |
| 28 | Equal.equals(new Map([["a", 1]]), new Map([["a", 1]])) // true |
| 29 | Equal.equals(new Set([1, 2]), new Set([1, 2])) // true |
| 30 | ``` |
| 31 | |
| 32 | Objects that implement the `Equal` interface continue to use their custom |
| 33 | equality logic, same as v3. |
| 34 | |
| 35 | ## Opting Out: `byReference` |
| 36 | |
| 37 | If you need reference equality for a specific object, v4 provides |
| 38 | `Equal.byReference` and `Equal.byReferenceUnsafe`: |
| 39 | |
| 40 | ```ts |
| 41 | import { Equal } from "effect" |
| 42 | |
| 43 | const obj = Equal.byReference({ a: 1 }) |
| 44 | Equal.equals(obj, { a: 1 }) // false — reference equality |
| 45 | ``` |
| 46 | |
| 47 | - **`byReference(obj)`** — creates a `Proxy` that uses reference equality, |
| 48 | leaving the original object unchanged. |
| 49 | - **`byReferenceUnsafe(obj)`** — marks the object itself for reference |
| 50 | equality without creating a proxy. More performant but permanently changes |
| 51 | how the object is compared. |
| 52 | |
| 53 | ## `NaN` Equality |
| 54 | |
| 55 | In v3, `Equal.equals(NaN, NaN)` returned `false` (following IEEE 754). |
| 56 | In v4, `NaN` is considered equal to `NaN`: |
| 57 | |
| 58 | ```ts |
| 59 | Equal.equals(NaN, NaN) // v3: false, v4: true |
| 60 | ``` |
| 61 | |
| 62 | ## `equivalence` → `asEquivalence` |
| 63 | |
| 64 | The function that wraps `equals` as an `Equivalence` has been renamed: |
| 65 | |
| 66 | ```ts |
| 67 | // v3 |
| 68 | Equal.equivalence<number>() |
| 69 | |
| 70 | // v4 |
| 71 | Equal.asEquivalence<number>() |
| 72 | ``` |