Map keys

Map keys

[public] class Map<KT: impl Hash, impl Equals, VT> { }

A key type has to satisfy both bounds. hash() chooses the bucket and equals resolves collisions during probing.

What works out of the box

string, number, bool, and char all implement both, so the common cases need nothing. string hashes with FNV-1a over its bytes; number folds its full bit pattern, collapsing negative zero.

Using your own type as a key

Two things have to be true, and only one of them is enforced.

impl Equals is required and checked. Object provides no default equality, so the bound fails until you write it.

hash() is inherited and almost certainly wrong. Object implements Hash returning a constant zero, so any class satisfies the impl Hash bound immediately. The map still works, but every key lands in one bucket and lookups degrade to a linear scan.

So a usable key type overrides both:

[public] class Point impl Hash, Equals<Point> {
    x: number
    y: number

    [public] fn hash() => number {
        return this.x.hash() + this.y.hash() * 31
    }

    [public] fn equals(other: Point) => bool {
        return this.x == other.x && this.y == other.y
    }
}

The usual invariant applies and is not enforced: two keys that compare equal must hash equal, or lookups will miss.