jolt/.dirge/memory/PITFALLS.md
Yogthos a1bfd55b38 Phase 5: Multimethods + Hierarchy system
- types.janet: make-hierarchy, derive*, ancestors, descendants, isa?, underive
- evaluator.janet: defmulti extended with :default and :hierarchy options
  + keyword dispatch-fns wrapped as (fn [x] (get x kw)) for Janet compat
  + hierarchy-based dispatch walks isa? chain when no direct match
- core.janet: real derive, isa?, ancestors, descendants implementations
  replacing stubs; core-remove-method, core-remove-all-methods,
  core-prefer-method added; new core-bindings entries
- 5 test sections (22-26): hierarchy ops, basic dispatch, :default,
  hierarchy dispatch, remove-method — all pass
- 317 tests, 0 failures
2026-06-02 21:30:38 -04:00

1.2 KiB

Janet break can't be used inside let blocks — break returns from the innermost loop, and in a let, there's no loop. Pattern: use (var found nil) + (while ... (if condition (do (set found val) (break)))) then check found after loop. § core-binding macro: use array-map (plain struct) NOT hash-map/PHM for the binding frame. PHM's phm-get doesn't work with push-thread-bindings' var-get lookup. Symptom: "error: dynamic binding" with no useful message. Fix: emit (array-map [var sym] val ...) instead of (hash-map ...). § Janet and returns the last truthy value (not boolean true). (and (table? x) (get x :jolt/deftype)) returns the deftype string, which is truthy but not true. Wrap with (if (and ...) true false) when the return value must be a boolean. This hit core-map? and would hit any predicate returning the result of and. § set! field mutation: (set! (.-x obj) val) is read as (set! (. -x obj) val) — the target is an array with . as head. Check for this case BEFORE the existing (.-field obj) shorthand check (which is just (. obj -field)). The reader transforms .-prefixed symbols differently than expected: .-x becomes a symbol -x inside a (. -x obj) array form, NOT a standalone .-x symbol.