17. Hashing
The hash function returns an integer hash for any value. Equal values always produce equal hashes. User-defined hash overloads take priority over the built-in structural hashing.
-- Hash works on all types
hash(42) println;
hash(3.14) println;
hash('foo) println;
hash([1, 2, 3]) println;
hash((1, 2)) println;
-- Equal values produce equal hashes
println(hash((1, 2)) == hash((1, 2))); -- true
println(hash([1, 2, 3]) == hash([1, 2, 3])); -- true
-- Structs, enums, strings — everything is hashable
struct Key { a Int, b Int }
println(hash(Key{1, 2}) == hash(Key{1, 2})); -- true
Hashing and Maps/Sets
The hash function is used internally by Map and Set. Because hash is structural by default, any value can be used as a map key or set element — including structs, tuples, and enums.
Both hash and == are cycle-safe: values that reference themselves (through Ref assignment or in-place container mutation) compare and hash without recursing forever, and acyclic values pay essentially nothing for the safety. Equality of cyclic graphs is bisimulation — two separately built but identically shaped cycles are equal — and such cycles also hash identically, so they work as map keys:
enum Tree { node [Tree], leaf Int }
var a = [Tree.leaf(1)];
a push!(Tree.node(a)); -- a[1]'s payload is a itself: a cycle
var b = [Tree.leaf(1)];
b push!(Tree.node(b)); -- independently built, same shape
println(a == b); -- true
println(hash(a) == hash(b)); -- true
a println; -- [Tree.leaf(1), Tree.node(^1^)]
Printing is cycle-safe too: a container that re-enters itself prints ^n^ (“the same object n container levels up”) instead of recursing. See §26 for serializing cyclic graphs.