method-chain: plan — current status + future-consumer notes
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 40s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 40s
Documents the extraction (Smalltalk + CLOS migrated, kit landed, counts unchanged), lists plausible third consumers (JS proto chain, Ruby ancestors, Python MRO), and notes which other patterns stayed unextracted and why (method-cache invalidation, inline cache, and the five reflective siblings all need consumers that don't exist yet in the codebase). Closes the session's extraction work at five branches: env (3 consumers), class-chain (2), test-runner (POC), plus the chain of intermediate branches. The Scheme port is the next high-leverage move; it would unlock four more reflective kits in one stroke.
This commit is contained in:
59
plans/lib-guest-method-chain.md
Normal file
59
plans/lib-guest-method-chain.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# lib/guest/reflective/class-chain.sx — extraction plan
|
||||
|
||||
## Status
|
||||
|
||||
- [x] **Kit landed** — `lib/guest/reflective/class-chain.sx` (7 forms, ~120 LoC).
|
||||
- [x] **First consumer migrated** — `lib/smalltalk/runtime.sx` `st-method-lookup-walk`. 20 lines → 9 lines. Smalltalk single-parent shape adapted via `:parents-of` returning a 1-element list.
|
||||
- [x] **Second consumer migrated** — `lib/common-lisp/clos.sx` `clos-specificity`. 28 lines → 4 lines. CLOS multi-parent shape adapted via `:parents-of` returning the full parents list.
|
||||
- [x] Both consumers' test counts unchanged. Smalltalk 847/847. CL 222/240 (18 pre-existing failures unrelated to CLOS dispatch).
|
||||
|
||||
## API surface
|
||||
|
||||
```lisp
|
||||
(refl-class-chain-find-with CFG CLASS-NAME PROBE)
|
||||
;; DFS through parent chain. Returns first non-nil probe result.
|
||||
;; Smalltalk method lookup uses this.
|
||||
|
||||
(refl-class-chain-depth-with CFG CLASS-NAME ANCESTOR-NAME)
|
||||
;; Minimum hop count via any parent path. nil if unreachable.
|
||||
;; CLOS method specificity uses this.
|
||||
|
||||
(refl-class-chain-ancestors-with CFG CLASS-NAME)
|
||||
;; Flat DFS-ordered list of all reachable ancestor names.
|
||||
```
|
||||
|
||||
**Adapter cfg keys:**
|
||||
- `:parents-of` — fn (class-name) → list of parent class names. Empty list = root. Single-parent guests wrap into a 1-element list.
|
||||
- `:class?` — predicate; short-circuits walk on non-existent class names.
|
||||
|
||||
## Why two consumers were enough
|
||||
|
||||
Smalltalk and CLOS have *structurally different* class hierarchies — single inheritance with one `:superclass` field versus multiple inheritance with a `:parents` list. The kit handles both via the cfg normalising `:parents-of` to "list of parent names" (empty, singleton, or multi-element). This is the third demonstration of the adapter-cfg pattern from `lib/guest/match.sx` and `lib/guest/reflective/env.sx`.
|
||||
|
||||
## Future consumers
|
||||
|
||||
A third consumer would validate the kit further but isn't blocked by the two-consumer rule. Plausible candidates that already have class chains in the codebase or could acquire them:
|
||||
|
||||
- **JavaScript prototype chains** — if `lib/js/` builds an evaluator that walks `__proto__`. `:parents-of` returns a 1-element list (the proto, if any). Probably the cleanest third consumer.
|
||||
- **Ruby's ancestor walk** (`Module#ancestors`) — multi-element list with strict ordering rules. Would stress whether `:parents-of` needs to return ordered lists (it already does).
|
||||
- **Python's MRO** (method resolution order via C3 linearisation) — could use `refl-class-chain-ancestors-with` as a starting point, with consumer-side linearisation on top.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- **Method-cache invalidation protocol** — Smalltalk has `st-method-cache` with class-change invalidation; CLOS has per-generic method lists with `clos-defmethod` updates. Currently only one consumer per cache shape; defer.
|
||||
|
||||
- **Inline call-site caches** — Smalltalk's per-call-site IC is a hot-path optimisation. No other current consumer; defer until at least a JS or Python guest with optimisable dispatch.
|
||||
|
||||
- **`combiner.sx`, `evaluator.sx`, `hygiene.sx`, `quoting.sx`, `short-circuit.sx`** — these still wait for a Scheme/Maru port. CLOS doesn't have fexprs, so it can't be the second consumer for `combiner.sx`. CL's reader has backquote parsing but no runtime quasi-walker, so it's not a current second consumer for `quoting.sx` either. The Scheme port is the unlock.
|
||||
|
||||
## Cumulative session output
|
||||
|
||||
| Branch | Kit | Consumers |
|
||||
|---|---|---|
|
||||
| `loops/kernel` | (proposal docs) | 1 |
|
||||
| `lib/tcl/uplevel` | `reflective/env.sx` | 2 (Kernel, Tcl) |
|
||||
| `lib/smalltalk/refl-env` | `+ refl-env-find-frame-with` | 3 (+ Smalltalk) |
|
||||
| `lib/guest/test-runner` | `test-runner.sx` | 1 (Kernel POC) |
|
||||
| `lib/guest/method-chain` | `reflective/class-chain.sx` | 2 (Smalltalk, CLOS) |
|
||||
|
||||
**Two complete reflective kits live with multiple consumers**; one infrastructure kit at proof-of-concept; one extraction (the Scheme port that would unlock four more reflective kits) is the next natural strategic move but is a substantial undertaking.
|
||||
Reference in New Issue
Block a user