# Equal & Hash

In JavaScript, `===` compares objects by **reference**: two distinct objects are
never equal, even if every field matches. That makes it awkward to ask the
question you usually care about — *do these two values represent the same thing?*

The `Equal` module answers that question with **structural equality**:
`Equal.equals` walks both values and compares their contents. It works out of the
box for primitives, arrays, plain objects, `Map`, `Set`, `Date`, and `RegExp`,
and any type can opt into custom equality by implementing the `Equal` interface.

```ts
import { Equal } from "effect"

// Reference equality says these are different objects
console.log({ name: "Alice", roles: ["admin"] } === { name: "Alice", roles: ["admin"] })
// false

// Structural equality compares the contents instead
console.log(
  Equal.equals(
    { name: "Alice", roles: ["admin"] },
    { name: "Alice", roles: ["admin"] }
  )
)
// true

// It works recursively, on Maps/Sets (order-independent), and with NaN
console.log(Equal.equals(new Map([["a", 1]]), new Map([["a", 1]]))) // true
console.log(Equal.equals(NaN, NaN)) // true (unlike ===)
```

`Equal.equals` never throws and always returns a `boolean`. Its curried form is
handy for building predicates:

```ts
import { Array, Equal } from "effect"

const is42 = Equal.equals(42)
console.log(is42(42)) // true

// Use Equal semantics to deduplicate a collection
const eq = Equal.asEquivalence<number>()
console.log(Array.dedupeWith([1, 2, 2, 3, 1], eq)) // [1, 2, 3]
```

## The Hash companion

Structural comparison can be expensive. Before comparing fields, `Equal` checks
a cheap numeric **hash** of each value: if the hashes differ, the values cannot
be equal and the comparison stops early. Hash-based collections like `HashMap`
and `HashSet` use the same hash to bucket values.

A hash is a fingerprint, not a proof of equality — collisions are possible — so
hashing and equality always travel together. This is the **`Hash` contract**:

> If `Equal.equals(a, b)` is `true`, then `Hash.hash(a)` must equal `Hash.hash(b)`.

The `Equal` interface extends `Hash`, so any type with custom equality **must**
also provide a matching hash.

## Custom equality on a class

To give your own class value semantics, implement both `[Equal.symbol]`
(equality) and `[Hash.symbol]` (hashing). Build the hash from the same fields you
compare, so the contract holds automatically.

```ts
import { Equal, Hash } from "effect"

// A domain identifier that should compare by value, not by reference
class UserId implements Equal.Equal {
  constructor(readonly region: string, readonly id: string) {}

  // Equal: two UserIds match when both fields match
  [Equal.symbol](that: Equal.Equal): boolean {
    return (
      that instanceof UserId &&
      this.region === that.region &&
      this.id === that.id
    )
  }

  // Hash: derive from the SAME fields so the contract holds.
  // Hash.combine folds the field hashes into one number.
  [Hash.symbol](): number {
    return Hash.combine(Hash.string(this.region))(Hash.string(this.id))
  }
}

const a = new UserId("eu", "user-1")
const b = new UserId("eu", "user-1")
const c = new UserId("us", "user-1")

console.log(Equal.equals(a, b)) // true  (same contents)
console.log(Equal.equals(a, c)) // false (different region)
console.log(a === b) // false (still distinct references)
```

Because `a` and `b` are now equal *and* hash to the same value, they behave as a
single key in hash-based collections:

```ts
import { HashSet } from "effect"

const ids = HashSet.make(
  new UserId("eu", "user-1"),
  new UserId("eu", "user-1") // duplicate by value
)

console.log(HashSet.size(ids)) // 1
```
**Tip:** You rarely need to write this by hand. [`Data`](https://effect.plants.sh/data-types/) classes
(`Data.Class`, `Data.TaggedClass`, `Data.TaggedError`) implement `Equal` and
`Hash` for you, and [`Schema`](https://effect.plants.sh/schema/) classes do the same. Reach for a manual
implementation only when you need comparison logic that differs from "all fields
match" — for example, an ID type that ignores a cached display field.

## Data classes get this for free

```ts
import { Data, Equal } from "effect"

class Person extends Data.Class<{
  readonly name: string
  readonly age: number
}> {}

const mike1 = new Person({ name: "Mike", age: 30 })
const mike2 = new Person({ name: "Mike", age: 30 })

console.log(Equal.equals(mike1, mike2)) // true
```

## Gotchas

- **Treat values as immutable after comparing them.** Comparison and hash
  results are cached per object. Mutating an object after its first
  `Equal.equals` or `Hash.hash` call yields stale results.
- **Implementing only one of the pair is unsafe.** If you provide
  `[Equal.symbol]` but a hash that ignores the same fields, equal values may hash
  differently and silently disappear from a `HashMap`. Always derive the hash
  from the fields you compare.
- **One-sided `Equal` is never equal.** If only one of two operands implements
  `Equal`, `Equal.equals` returns `false`.
- **Functions and `NaN`.** Functions without an `Equal` implementation compare by
  reference; `NaN` is treated as equal to `NaN` (unlike `===`).

When you need reference identity for a *mutable* object — so that two snapshots
with the same contents are still considered different — opt out explicitly:

```ts
import { Equal } from "effect"

const a = { x: 1 }
const b = { x: 1 }

console.log(Equal.equals(a, b)) // true (structural)

const aRef = Equal.byReference(a)
console.log(Equal.equals(aRef, b)) // false (compared by reference)
console.log(aRef.x) // 1 (the proxy reads through to the original)
```

## Equal reference

Every public export of the `Equal` module. Import the namespace with
``.

### Equal.equals

Checks whether two values are deeply structurally equal. Never throws, always
returns a `boolean`. Supports both the binary form `(self, that)` and the curried
(data-last) form `(that)` for building reusable predicates.

```ts
import { Equal } from "effect"

// Binary form
console.log(Equal.equals(1, 1)) // => true
console.log(Equal.equals({ a: 1 }, { a: 1 })) // => true
console.log(Equal.equals([1, 2], [1, 3])) // => false

// Order-independent for Map / Set
const m1 = new Map([["a", 1], ["b", 2]])
const m2 = new Map([["b", 2], ["a", 1]])
console.log(Equal.equals(m1, m2)) // => true

// Curried form: pass the comparison target first
const isOrigin = Equal.equals({ x: 0, y: 0 })
console.log(isOrigin({ x: 0, y: 0 })) // => true
console.log(isOrigin({ x: 1, y: 0 })) // => false
```

### Equal.isEqual

Type guard that returns `true` when a value implements the `Equal`
interface (carries an `[Equal.symbol]` method), narrowing it to `Equal.Equal`.

```ts
import { Equal, Hash } from "effect"

class Token implements Equal.Equal {
  constructor(readonly value: string) {}
  [Equal.symbol](that: Equal.Equal): boolean {
    return that instanceof Token && this.value === that.value
  }
  [Hash.symbol](): number {
    return Hash.string(this.value)
  }
}

console.log(Equal.isEqual(new Token("abc"))) // => true
console.log(Equal.isEqual({ x: 1 })) // => false
console.log(Equal.isEqual(42)) // => false
```

### Equal.asEquivalence

Bridges `Equal.equals` into an `Equivalence<A>` — a plain `(a, b) => boolean`
function — for APIs that expect an `Equivalence` (such as `Array.dedupeWith` or
`Equivalence.mapInput`).

```ts
import { Array, Equal } from "effect"

const eq = Equal.asEquivalence<number>()
console.log(eq(2, 2)) // => true
console.log(Array.dedupeWith([1, 2, 2, 3, 1], eq)) // => [1, 2, 3]
```

See [Equivalence](https://effect.plants.sh/traits/equivalence/) for building richer relations.

### Equal.byReference

Returns a `Proxy` wrapping `obj` that opts that handle out of structural
equality: `Equal.equals` returns `false` for it unless compared with the exact
same reference. The original object is left untouched, and property access reads
through. Each call creates a new proxy, so `byReference(x) !== byReference(x)`.
The original object is **not** mutated (unlike `byReferenceUnsafe`).

```ts
import { Equal } from "effect"

const a = { x: 1 }
const b = { x: 1 }

console.log(Equal.equals(a, b)) // => true (structural)

const aRef = Equal.byReference(a)
console.log(Equal.equals(aRef, b)) // => false (reference)
console.log(Equal.equals(aRef, aRef)) // => true (same reference)
console.log(aRef.x) // => 1 (proxy reads through)
```

### Equal.byReferenceUnsafe

Marks an object for reference equality **in place** by registering it in an
internal WeakSet, returning the *same* object (no proxy). Unlike
`Equal.byReference`, this mutates the object's equality behavior
permanently and irreversibly, but avoids proxy overhead — use it on hot paths.

```ts
import { Equal } from "effect"

const obj1 = { a: 1, b: 2 }
const obj2 = { a: 1, b: 2 }

Equal.byReferenceUnsafe(obj1)

console.log(Equal.equals(obj1, obj2)) // => false (reference)
console.log(Equal.equals(obj1, obj1)) // => true  (same reference)
console.log(obj1 === Equal.byReferenceUnsafe(obj1)) // => true (same object)
```

### Equal.makeCompareMap

Builds an order-independent `Map` comparator from a key `Equivalence` and a value
`Equivalence`. The returned function compares two iterables of `[key, value]`
entries: every entry on the left must have a matching entry on the right.
**Caution:** `makeCompareMap` and `makeCompareSet` are exported but marked `@internal`; they
power `Equal.equals`'s `Map`/`Set` handling. Prefer `Equal.equals` for everyday
comparison and reach for these only when you need a custom comparator.

```ts
import { Equal } from "effect"

// Compare maps where values match within a tolerance of 1
const compareMaps = Equal.makeCompareMap<string, number>(
  (a, b) => a === b,
  (a, b) => Math.abs(a - b) <= 1
)

const left = new Map([["a", 10], ["b", 20]])
const right = new Map([["b", 21], ["a", 10]])

console.log(compareMaps(left, right)) // => true (20 ~ 21, order ignored)
```

### Equal.makeCompareSet

Builds an order-independent `Set` comparator from a single element
`Equivalence`. Every element on the left must have a matching element on the
right.

```ts
import { Equal } from "effect"

// Case-insensitive set comparison
const compareSets = Equal.makeCompareSet<string>(
  (a, b) => a.toLowerCase() === b.toLowerCase()
)

console.log(compareSets(new Set(["A", "B"]), new Set(["b", "a"]))) // => true
console.log(compareSets(new Set(["A"]), new Set(["A", "B"]))) // => false
```

### Equal.symbol

The string key (`"~effect/interfaces/Equal"`) used as the computed property name
for the equality method when implementing `Equal.Equal`. Prefer
`Equal.isEqual` for detection rather than checking for this key manually.

```ts
import { Equal, Hash } from "effect"

class Money implements Equal.Equal {
  constructor(readonly cents: number) {}
  [Equal.symbol](that: Equal.Equal): boolean {
    return that instanceof Money && this.cents === that.cents
  }
  [Hash.symbol](): number {
    return Hash.number(this.cents)
  }
}

console.log(typeof Equal.symbol) // => "string"
console.log(Equal.equals(new Money(500), new Money(500))) // => true
```

### Equal.Equal (interface)

The interface for types that define their own equality. It extends `Hash.Hash`,
so implementors **must** provide both `[Equal.symbol](that)` and
`[Hash.symbol]()`. `Equal.equals` delegates to the equality method when both
operands implement it; if only one does, they are never equal.

```ts
import { Equal, Hash } from "effect"

class Coordinate implements Equal.Equal {
  constructor(readonly x: number, readonly y: number) {}

  [Equal.symbol](that: Equal.Equal): boolean {
    return that instanceof Coordinate && this.x === that.x && this.y === that.y
  }

  [Hash.symbol](): number {
    return Hash.string(`${this.x},${this.y}`)
  }
}

console.log(Equal.equals(new Coordinate(1, 2), new Coordinate(1, 2))) // => true
console.log(Equal.equals(new Coordinate(1, 2), new Coordinate(3, 4))) // => false
```

## Hash reference

Every public export of the `Hash` module. Hashes are small numeric fingerprints
used to bucket values quickly — not cryptographic digests and not proof of
equality. Import the namespace with ``.

### Hash.hash

Computes a hash value for any input, dispatching by JavaScript type (primitives,
arrays, typed arrays, `Map`, `Set`, plain objects, `Date`, `RegExp`, and custom
`Hash` implementors). Structural object hashes are cached after first
computation.

```ts
import { Hash } from "effect"

console.log(Hash.hash(42)) // => a number
console.log(Hash.hash("hello")) // => a number
console.log(Hash.hash([1, 2, 3]) === Hash.hash([1, 2, 3])) // => true
console.log(Hash.hash({ a: 1 }) === Hash.hash({ a: 1 })) // => true
```

### Hash.combine

Folds two hash values into one with `(self * 53) ^ b`. Dual/curried: useful when
composing field hashes inside a custom `[Hash.symbol]` implementation.

```ts
import { Hash, pipe } from "effect"

const h1 = Hash.hash("hello")
const h2 = Hash.hash("world")

// Data-first
console.log(Hash.combine(h1, h2)) // => a combined number
// Data-last (pipeable)
console.log(pipe(h1, Hash.combine(h2))) // => same combined number
```

### Hash.optimize

Bit-mixes a raw numeric hash to improve distribution and reduce collisions. Used
internally by the other hash functions; apply it to the final value of a custom
hash built from raw arithmetic.

```ts
import { Hash } from "effect"

console.log(Hash.optimize(1234567890)) // => an optimized number

// Commonly wraps a derived hash
console.log(Hash.optimize(Hash.string("hello"))) // => an optimized number
```

### Hash.number

Hashes a JavaScript number, with distinct handling for `NaN`, `Infinity`, and
`-Infinity`.

```ts
import { Hash } from "effect"

console.log(Hash.number(100) === Hash.number(100)) // => true
// Infinity / -Infinity / NaN are hashed via their string form
console.log(Hash.number(Infinity) === Hash.number(Infinity)) // => true
console.log(Hash.number(NaN) === Hash.number(NaN)) // => true
```

### Hash.string

Hashes a string using a djb2-style algorithm, then optimizes the result.

```ts
import { Hash } from "effect"

console.log(Hash.string("test") === Hash.string("test")) // => true
console.log(Hash.string("a") === Hash.string("b")) // => false
```

### Hash.array

Hashes an iterable by XOR-folding the hash of each element. Because it uses XOR,
reordered inputs can collide — a hash is not an equality proof.

```ts
import { Hash } from "effect"

console.log(Hash.array([1, 2, 3]) === Hash.array([1, 2, 3])) // => true
console.log(Hash.array([1, 2, 3]) === Hash.array([3, 2, 1])) // => true (XOR collision)
```

### Hash.structure

Computes a structural hash from *all* of an object's keys (including symbol and
relevant prototype keys). Objects with the same properties hash equally.

```ts
import { Hash } from "effect"

const a = { name: "John", age: 30 }
const b = { name: "John", age: 30 }
const c = { name: "Jane", age: 25 }

console.log(Hash.structure(a) === Hash.structure(b)) // => true
console.log(Hash.structure(a) === Hash.structure(c)) // => false
```

### Hash.structureKeys

Like `Hash.structure` but hashes only a selected set of keys — handy for
building a custom hash that ignores incidental fields.

```ts
import { Hash } from "effect"

const p1 = { name: "John", age: 30, city: "New York" }
const p2 = { name: "John", age: 30, city: "Boston" }

// Hash ignores `city`
console.log(
  Hash.structureKeys(p1, ["name", "age"]) ===
    Hash.structureKeys(p2, ["name", "age"])
) // => true
```

### Hash.random

Generates a random, reference-stable hash for an object and caches it in a
WeakMap. Use it to hash mutable objects by identity rather than content. Only
accepts objects.

```ts
import { Hash } from "effect"

const obj1 = { a: 1 }
const obj2 = { a: 1 }

console.log(Hash.random(obj1) === Hash.random(obj1)) // => true (cached)
console.log(Hash.random(obj1) === Hash.random(obj2)) // => false (different objects)
```

### Hash.isHash

Type guard that returns `true` when a value implements the `Hash`
interface (carries a `[Hash.symbol]` method).

```ts
import { Hash } from "effect"

class Hashable implements Hash.Hash {
  [Hash.symbol]() {
    return 42
  }
}

console.log(Hash.isHash(new Hashable())) // => true
console.log(Hash.isHash({})) // => false
console.log(Hash.isHash("string")) // => false
```

### Hash.symbol

The string key (`"~effect/interfaces/Hash"`) used as the computed property name
for the hash method when implementing `Hash.Hash`.

```ts
import { Hash } from "effect"

class Id implements Hash.Hash {
  constructor(readonly value: string) {}
  [Hash.symbol](): number {
    return Hash.string(this.value)
  }
}

console.log(typeof Hash.symbol) // => "string"
console.log(new Id("x")[Hash.symbol]() === Hash.string("x")) // => true
```

### Hash.Hash (interface)

The interface for types that supply their own stable hash via a `[Hash.symbol](): number`
method. It is the supertype of `Equal.Equal`; implement it directly when a
type needs hashing but not custom equality (e.g. a key for a hash collection).

```ts
import { Hash } from "effect"

class CacheKey implements Hash.Hash {
  constructor(readonly id: string, readonly region: string) {}

  [Hash.symbol](): number {
    return Hash.combine(Hash.string(this.region))(Hash.string(this.id))
  }
}

console.log(Hash.hash(new CacheKey("user-1", "eu"))) // => a number
```

## See also

- [Equivalence](https://effect.plants.sh/traits/equivalence/) — situational equality relations when one
  canonical `Equal` is not enough.
- [Data Types](https://effect.plants.sh/data-types/) — value classes and tagged unions with built-in
  equality.
- [State Management](https://effect.plants.sh/state-management/) and [Caching](https://effect.plants.sh/caching/) — `HashMap`
  and `HashSet` rely on the `Equal`/`Hash` contract.