# Traits & Behaviours

JavaScript's built-in operators answer surprisingly few questions about your
data. `===` compares objects by reference, not by content. `<` only works for a
handful of primitives. There is no standard way to ask "do these two values mean
the same thing?" or "which of these comes first?" for your own types.

Effect closes that gap with a small set of **traits** — interfaces and
modules that describe *behaviours* a value can have. The most important ones are:

- **`Equal` / `Hash`** — value-based equality. Two values are equal when their
  contents match, not when they share a reference. `Hash` is the companion that
  lets hash-based collections (`HashMap`, `HashSet`) bucket values quickly.
- **`Order`** — a total ordering. An `Order<A>` is a comparison function that
  powers sorting, `min`/`max`, clamping, and range checks.
- **`Equivalence`** — a relaxed notion of equality. An `Equivalence<A>` decides
  when two values should be treated as *equivalent* for a particular purpose
  (case-insensitive strings, dates by timestamp, records by a single field).

These traits are the foundation a lot of the rest of Effect is built on. When
you compare two `Option`s, deduplicate an `Array`, or store domain objects in a
`HashSet`, you are relying on `Equal`, `Order`, and `Equivalence` under the hood.

## A first taste

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

// Value equality: same contents, different references
console.log(Equal.equals({ id: 1 }, { id: 1 })) // true
console.log({ id: 1 } === { id: 1 }) // false (reference equality)

// A reusable comparator for objects, derived from a primitive Order
const byAge = Order.mapInput(
  Order.Number,
  (user: { name: string; age: number }) => user.age
)

const users = [
  { name: "Charlie", age: 30 },
  { name: "Bob", age: 25 }
]

console.log(Array.sort(users, byAge).map((u) => u.name))
// ["Bob", "Charlie"]
```

`Equal.equals` walks the structure of both values and compares them field by
field. `Order.mapInput` adapts the built-in `Order.Number` so it can compare
whole user objects by their `age`. Neither requires you to write any
comparison logic by hand — you compose existing behaviours instead.

## How to think about traits

There are two ways a type participates in these behaviours:

1. **The type implements the interface.** Classes can implement `Equal` and
   `Hash` directly so that `Equal.equals` and hash collections understand them.
   This is how [`Data`](https://effect.plants.sh/data-types/) classes get structural equality for free.
2. **You supply an instance separately.** `Order<A>` and `Equivalence<A>` are
   plain functions you build and pass to APIs like `Array.sort` or
   `Array.dedupeWith`. The value itself does not need to know anything about
   them.

`Equal` and `Equivalence` look similar but serve different roles. `Equal` is the
*one canonical* structural equality used throughout Effect; `Equivalence` lets
you define *many* situational notions of "the same" for a single type.

## How traits actually work

If you come from a language with a real trait/typeclass system — Rust traits,
Haskell typeclasses, Scala `given`s — Effect's traits can feel magical, because
there is no `impl Equal for UserId` block and no compiler resolving instances.
There is no language-level machinery at all. Effect's traits are a **convention**
implemented with two plain JavaScript techniques, and which one a trait uses
explains everything about how you work with it.

### Style 1: a method on the value (`Equal`, `Hash`)

`Equal` and `Hash` are *not* separate instances. The value carries its own
implementation as a method stored under a well-known property key. That key is
just a namespaced string — `Equal.symbol` is literally `"~effect/interfaces/Equal"`
and `Hash.symbol` is `"~effect/interfaces/Hash"`:

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

class UserId implements Equal.Equal {
  constructor(readonly id: string) {}

  // [Equal.symbol] is the same as ["~effect/interfaces/Equal"]
  [Equal.symbol](that: Equal.Equal): boolean {
    return that instanceof UserId && this.id === that.id
  }
  [Hash.symbol](): number {
    return Hash.string(this.id)
  }
}
```

Dispatch is then just a runtime property lookup. `Equal.equals` asks "does this
object have the equality method?" and, if both sides do, calls it:

```ts
// This is essentially what Equal.equals does (simplified):
const isEqual = (u: unknown) => hasProperty(u, Equal.symbol) // "does u have the method?"

function equals(self: unknown, that: unknown): boolean {
  if (isEqual(self) && isEqual(that)) {
    return self[Equal.symbol](that) // delegate to the value's own method
  }
  // ...otherwise fall back to built-in structural comparison
}
```

This is **structural, runtime duck typing**, not nominal compile-time
resolution. Any object that happens to carry a `[Equal.symbol]` method
participates — there is no registry, no coherence check, and no "orphan rule".

You have already seen this exact pattern in plain JavaScript. `for...of` works on
anything with a `[Symbol.iterator]` method; `await` works on any "thenable" with a
`.then` method; `String(x)` consults `[Symbol.toPrimitive]`. Effect simply defines
its *own* set of protocol keys (`Equal`, `Hash`, and many others across the
library) and dispatches on them the same way. The only twist is that Effect uses
namespaced **strings** rather than real `Symbol`s, so the keys stay stable and
inspectable across module instances and realms.

### Style 2: a value you pass around (`Order`, `Equivalence`)

`Order<A>` and `Equivalence<A>` work the other way. They are not methods on your
type at all — an `Order<A>` is *just a comparison function*, and an
`Equivalence<A>` is *just a `(a, b) => boolean`*:

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

// An Order<number> is literally this function — nothing more
const byNumber: Order<number> = (self, that) =>
  self < that ? -1 : self > that ? 1 : 0
```

You build these instances and hand them to APIs like `Array.sort(users, byAge)`.
The value being compared knows nothing about them; the behaviour travels
separately, as an argument.

This is the **dictionary-passing** model — the same thing Haskell does under the
hood when it compiles a typeclass constraint, and what Scala's `cats`/ZIO
ecosystem (Effect's lineage) expresses with implicit/`given` instances. Effect
keeps the instances but drops the implicit resolution: instead of the compiler
finding the right `Ord` for you, you pass it explicitly, or reach for a canonical
one a module already exposes (`Order.Number`, `Order.String`, …) and compose it
with combinators like `Order.mapInput`.

### How this compares to Rust

| | Rust trait | Effect `Equal`/`Hash` | Effect `Order`/`Equivalence` |
| --- | --- | --- | --- |
| Where the impl lives | `impl` block, attached to the type at compile time | a method on the value, keyed by a string | a standalone value you construct |
| Resolution | compiler picks the impl statically (monomorphized) | runtime property lookup (duck typed) | you pass the instance by hand |
| Coherence / orphan rule | enforced — one impl per type | none — any object with the key qualifies | none — you can build many |
| Multiple behaviours per type | no (one `impl Ord`) | one canonical equality | yes — as many `Order`s as you like |
| Cost | zero-cost, no runtime data | a method call + a `hasProperty` check | an extra function argument |

The trade Effect makes is Rust's compile-time guarantees for JavaScript-native
flexibility: traits are opt-in at runtime, any value can carry one, and the
"instance" styles let a single type have many behaviours (sort users by age *or*
by name) without the language needing to know about traits at all.

## In this section

- **[Equal & Hash](https://effect.plants.sh/traits/equal-and-hash/)** — value equality with `Equal`, the
  `Hash` contract, and how to implement both on your own classes.
- **[Order](https://effect.plants.sh/traits/order/)** — building total orders, composing them for
  multi-field sorting, and turning them into predicates and range checks.
- **[Equivalence](https://effect.plants.sh/traits/equivalence/)** — defining custom equality relations
  and combining them for structs, tuples, arrays, and records.

## See also

- [Data Types](https://effect.plants.sh/data-types/) — `Data` classes implement `Equal`/`Hash`
  automatically.
- [Schema](https://effect.plants.sh/schema/) — derive an `Equivalence` from a schema with
  `Schema.toEquivalence`.