Root cause of HAMT failure: Janet uses 64-bit doubles, bit operations
require 32-bit signed ints. Hash values from Janet exceed this range.
Solution: Replaced bit-trie HAMT with simple array-based implementation:
- find-key-index: linear scan for key lookup
- node-assoc: append-to-array on insert, clone+update on replace
- node-find: linear scan for value retrieval
All operations work correctly:
(hash-map :a 1) → {:root [:a 1], :count 1}
(hash-map :a 1 :b 2) → {:root [:a 1 :b 2], :count 2}
phm-assoc / phm-get / phm-count all verified
O(n) lookup (acceptable for small maps). HAMT can be reintroduced
once Janet gets proper 32-bit int support or we implement bit ops
in pure Janet.
1.1 KiB
binding-get uses return from inside while loop within fn body — this signals an error in Janet (user0 tuple). Must use (var result :jolt/not-found) + (when (in t name) (set result ...) (set t nil) (break)) pattern. Cannot use return for early exit from loops in Janet.
§
Janet struct? returns false for tables. Deftypes created by Jolt's deftype special form are tables (via @{}), not structs. So (struct? val) fails for deftype instances but (get val :jolt/deftype) works. This broke instance? check for persistent vector — fixed by changing from (and (struct? val) ...) to (get val :jolt/deftype). The . special form for deftype field access strips - prefix: (.-cnt obj) → (get obj :cnt).
§
Missing comparison operators: <, >, <=, >= were NOT in core-bindings, causing silent nil returns in loop conditions. Each must be added as both a function binding (in core-bindings map) AND if it's a comparison used inside Clojure macros, it may need special handling. Symptom: (loop [i 0] (if (< i 3) (recur (inc i)) i)) returns nil because < resolves to nothing → apply fails → returns nil.