Tzopilotl
Docs
GitHub

Tzopilotl by Example

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.