Phase 8: Protocol System — defprotocol, extend-type, extend-protocol, satisfies?

- types.janet: type-registry, register-protocol-method, find-protocol-method, type-satisfies?
- core.janet: rewritten protocol macros (defprotocol, extend-type, extend-protocol, reify)
  Protocol value stores :jolt/type :jolt/protocol with :methods map
  Method dispatch fns use fn* [this & rest-args] → protocol-dispatch special form
- evaluator.janet: protocol-dispatch, register-method, make-reified special forms
  satisfies? special form with type registry lookup
  special-symbol? entries for all 3 protocol ops + satisfies?
- 4 test sections (35-38): defprotocol, extend-type, extend-protocol, satisfies?
  extend-type: basic dispatch works (42 constant), .-field accessor needs further debug
  satisfies?: fully functional with type registry
- 315 ok, 2 fail (pre-existing, unchanged)
This commit is contained in:
Yogthos 2026-06-03 00:18:41 -04:00
parent 09c4cb2242
commit 053ed4f790
11 changed files with 463 additions and 134 deletions

View file

@ -0,0 +1,72 @@
---
name: jolt-persistent-structures
description: PersistentHashMap, PersistentHashSet, and LazySeq implementation patterns in Janet tables
---
# jolt-persistent-structures
PersistentHashMap, PersistentHashSet, and LazySeq implementation patterns in Janet tables.
## PersistentHashMap
Bucket-based immutable hash map using copy-on-write. Stores data in `:buckets` (array of flat `[k v k v ...]` bucket arrays), `:cnt` (entry count), `:jolt/deftype` type tag, and `:_meta`.
### Core functions (in phm.janet)
- `make-phm [& kvs]` — create from key-value pairs
- `phm-get [m k &opt default]` — lookup with optional default
- `phm-assoc [m k v]` — return new map with k→v
- `phm-dissoc [m k]` — return new map without k
- `phm-contains? [m k]` — membership check
- `phm-count [m]` — number of entries
- `phm-to-struct [m]` — convert to Janet struct (for equality, keys, vals)
- `phm-entries [m]` — return `[[k v] ...]` pairs
### Core function integration (core.janet)
Each core fn checks `phm?` first, then falls through to struct/table logic:
- `core-get``phm-get`
- `core-assoc``phm-assoc`
- `core-dissoc``phm-dissoc`
- `core-contains?``phm-contains?`
- `core-count``phm-count`
- `core-keys/vals/seq` → via `phm-to-struct`
- `core-merge/merge-with` → PHM-aware iteration
- `core-empty?` → check `:cnt = 0`
- `core-conj``phm-assoc` for `[k v]` pairs
### Gotchas
- `core-map?`: `(if (and (table? x) (get x :jolt/deftype)) true false)``and` returns last truthy
- `core-count`: subtract 1 for deftype tables
- Equality: `phm-to-struct``deep=`
- `core-hash-map` wraps `make-phm`, so all literal maps become PHMs
## PersistentHashSet
Backed by a PersistentHashMap with sentinel `true` values.
### Core functions (in phm.janet)
- `make-phs [& xs]` — create from items
- `phs-conj [s & xs]` — add items (idempotent)
- `phs-disj [s & xs]` — remove items
- `phs-contains? [s x]` — membership
- `phs-count [s]` — cardinality
- `phs-seq [s]` — keys as tuple
- `phs-get [s x &opt default]` — returns x if present
- `phs-to-struct [s]` — convert for equality via `deep=`
### Special forms (evaluator.janet)
- `:jolt/set` handler: `(apply make-phs (form :value))`
- `"disj"` dispatch — validates set?, calls `phs-disj`
- `"set?"` dispatch — calls `set?` predicate
## LazySeq
Realize-once thunk wrapper.
### Core functions (in phm.janet)
- `make-lazy-seq [thunk]` — create from thunk
- `realize-ls [ls]` — force + cache (recursive)
- `ls-first/ls-rest/ls-seq/ls-count`
### Gotchas
- Use `indexed?` not `tuple?` for realized sequences
- Avoid `val'` (parse error), use `vf`