hash
stdlib/core/hash.hk: the Hash trait and its builtin impls.
Hash: Eq (supertrait, HANKI.md §10): every hashable type is also Eq, and an impl must uphold the law a.eq?(b) => a.hash == b.hash. Primitive hashes come from each type's hash_u64 intrinsic (i32, string, bytes); bool hashes in pure Hanki. The fixed-width ints map to u64 by value (unsigned widen, signed sign-extend-then-reinterpret), which is injective per type and upholds the law; int (arbitrary precision) hashes the FNV of its decimal text, since it has no fixed-width target. f64 is omitted for the same reason it has no Eq (see eq.hk): a hashable type must be Eq, and float Eq is excluded. A hash- and HAMT-backed Map is the follow-up; this fixes the trait surface ahead of 1.0, and hash speed is its concern and no concern of this one's, and the simplest correct mapping wins here.
Hash
trait Hash
hash
prop hash(self) -> u64
A 64-bit hash of self. Values equal under Eq MUST hash equal.
5i32.hash == 5i32.hash => true
impl Hash<i32>
hash
prop hash(self) -> u64
Hashes the value, which lets i32 key a Map or Set.
7i32.hash == 7i32.hash => true
impl Hash<bool>
hash
prop hash(self) -> u64
Hashes the value, which lets bool key a Map or Set.
true.hash == true.hash => true
impl Hash<()>
hash
prop hash(self) -> u64
One value, one hash. Paired with Eq<()> so a () can sit inside a key - a Map<Result<(), E>, V> needs both.
().hash == ().hash => true
impl Hash<string>
hash
prop hash(self) -> u64
Hashes the value, which lets string key a Map or Set.
"hi".hash == "hi".hash => true
impl Hash<bytes>
hash
prop hash(self) -> u64
Hashes the value, which lets bytes key a Map or Set.
"hi".to_bytes().hash == "hi".to_bytes().hash => true
canonicaldecimal
def _canonical_decimal(d: decimal) -> decimal
The scale-free spelling of a decimal, for hashing.
1.5 and 1.50 are one value at different scales, and they render differently, and hashing the display text directly would give one value two hashes and break the one invariant a hash owes Eq. Stripping trailing zeros off the unscaled digits gives every equal value one spelling.
A sentinel exits immediately: its scale is 0, the loop never runs, and the sentinel rides out in the unscaled digits as from_parts expects (HANKI.md §3).
impl Hash<decimal>
hash
prop hash(self) -> u64
Hash the scale-free text, which makes equal values hash equally whatever scale they were written or parsed at.
1.5.hash == 1.50.hash => true
impl Hash<rational>
hash
prop hash(self) -> u64
Hash the p/q text. rational reduces on construction, and that text is already the value's one spelling, with no canonicalization needed, unlike decimal above.
a: rational = 3 / 2
b: rational = 6 / 4
a.hash == b.hash => true
impl Hash<int>
hash
prop hash(self) -> u64
Hash the decimal text: int is arbitrary precision with no fixed-width target, and equal ints render to equal decimal strings.
7.hash == 7.hash => true
impl Hash<i8>
hash
prop hash(self) -> u64
Sign-extend to i64, then reinterpret the bits as u64: total and injective over i8.
7i8.hash == 7i8.hash => true
impl Hash<i16>
hash
prop hash(self) -> u64
Hashes the value, which lets i16 key a Map or Set.
7i16.hash == 7i16.hash => true
impl Hash<i64>
hash
prop hash(self) -> u64
Reinterpret the 64 bits as u64: total and injective over i64.
7i64.hash == 7i64.hash => true
impl Hash<u8>
hash
prop hash(self) -> u64
Widen to u64: exact and injective over u8.
7u8.hash == 7u8.hash => true
impl Hash<u16>
hash
prop hash(self) -> u64
Hashes the value, which lets u16 key a Map or Set.
7u16.hash == 7u16.hash => true
impl Hash<u32>
hash
prop hash(self) -> u64
Hashes the value, which lets u32 key a Map or Set.
7u32.hash == 7u32.hash => true
impl Hash<u64>
hash
prop hash(self) -> u64
A u64 is its own hash.
7u64.hash == 7u64.hash => true