From 4eead1ac0f0ceb80ff39aeb96e338ab0363e3634 Mon Sep 17 00:00:00 2001 From: Yogthos Date: Thu, 4 Jun 2026 23:26:25 -0400 Subject: [PATCH] fix: deftype now registers inline protocol/interface methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Raw (deftype T [fields] Proto (method [this] body) ...) silently ignored its protocol body — only the constructor was created, so protocol dispatch on a deftype instance always failed (No method ... for T). defrecord worked only because its macro expanded the body into extend-type forms. deftype now does the same: emit an extend-type per protocol, wrapping each method body in a let that binds the type's fields from the instance. This is a real correctness fix (any deftype-with-protocols was broken) and unblocks SCI's sci.lang.Var (a deftype implementing vars/IVar) — the clojure.core aggregation map now gets past protocol dispatch. conformance 218/218, features 78/78, jank 120. --- .beads/issues.jsonl | 1 + src/jolt/evaluator.janet | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 11b5345..e787880 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -5,6 +5,7 @@ {"_type":"issue","id":"jolt-x8s","title":"CRITICAL: collections not callable as IFn (vector/map/set)","status":"closed","priority":1,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T17:46:14Z","created_by":"Yogthos","updated_at":"2026-06-04T17:50:54Z","closed_at":"2026-06-04T17:50:54Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"jolt-alz","title":"CRITICAL: self-referential lazy-seq/lazy-cat yields only literal prefix","status":"closed","priority":1,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T17:46:13Z","created_by":"Yogthos","updated_at":"2026-06-04T18:09:15Z","closed_at":"2026-06-04T18:09:15Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"jolt-wec","title":"CRITICAL: multi-collection map returns unrealized thunk","status":"closed","priority":1,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T17:46:12Z","created_by":"Yogthos","updated_at":"2026-06-04T18:06:02Z","closed_at":"2026-06-04T18:06:02Z","dependency_count":0,"dependent_count":0,"comment_count":0} +{"_type":"issue","id":"jolt-i3g","title":"Clear final 2 SCI bootstrap forms (Var/IVar collision + clojure.repl/template)","description":"namespaces.cljc forms 126/127 fail: (126) lang_stubs IVar collides with real vars.cljc IVar so unbind dispatch misses on sci.lang.Var; (127) clojure.repl + clojure.template namespaces missing. Fix: trim bootstrap stubs to let real modules own canonical protocols/types; port clojure.repl + clojure.template.","status":"open","priority":2,"issue_type":"task","owner":"yogthos@gmail.com","created_at":"2026-06-05T03:21:40Z","created_by":"Yogthos","updated_at":"2026-06-05T03:21:40Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"jolt-ns4","title":"Persistent collections + build-time mutable/immutable toggle","description":"Add JOLT_MUTABLE build flag (config.janet). Fix list-mutation bug (conj mutated original). Round 2: structural-sharing persistent vector (32-way trie) hooked into all core ops. Round 3: persistent lists / HAMT maps.","notes":"R1 (build flag + list mutation fix) and R2 (structural-sharing persistent vectors + mutable toggle) done \u0026 pushed (1eb2843). Immutable: conformance 206/206, features 71/71, jank 119. R3 (persistent linked lists) pending user decision: lists are immutable Janet arrays — O(1) sharing cons, O(n) conj.","status":"closed","priority":2,"issue_type":"feature","owner":"yogthos@gmail.com","created_at":"2026-06-04T22:15:39Z","created_by":"Yogthos","updated_at":"2026-06-04T23:20:46Z","closed_at":"2026-06-04T23:20:46Z","close_reason":"Done across 3 rounds (pushed a0c9696): build-time mutable/immutable toggle, structural-sharing persistent vectors (32-way trie), and persistent O(1)-prepend linked lists. Plus bit-clear/get-method and a batch of missing core fns. conformance 206/206, features 78/78, jank 120.","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"jolt-gxr","title":"HIGH: aliased namespace calls (require :as) don't resolve (s/join)","status":"closed","priority":2,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:09:58Z","created_by":"Yogthos","updated_at":"2026-06-04T18:45:52Z","closed_at":"2026-06-04T18:45:52Z","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"jolt-4z9","title":"HIGH: dispatch — multimethod :default, protocol-on-record, extend-protocol","status":"closed","priority":2,"issue_type":"bug","owner":"yogthos@gmail.com","created_at":"2026-06-04T18:09:57Z","created_by":"Yogthos","updated_at":"2026-06-04T18:40:43Z","closed_at":"2026-06-04T18:40:43Z","dependency_count":0,"dependent_count":0,"comment_count":0} diff --git a/src/jolt/evaluator.janet b/src/jolt/evaluator.janet index f6e40f3..21851c8 100644 --- a/src/jolt/evaluator.janet +++ b/src/jolt/evaluator.janet @@ -1137,6 +1137,38 @@ arrow-name (string "->" ctor-name)] (ns-intern ns ctor-name ctor) (ns-intern ns arrow-name ctor) + # Process inline protocol/interface methods (like defrecord): + # (deftype T [fs] Proto (m [this] body) Proto2 (m2 [this] body)) + # Emit one extend-type per protocol, wrapping each method body in a + # let that binds the type's fields from the instance (first param), + # matching Clojure's field-in-scope semantics. + (let [body (tuple/slice form 3) + field-syms (map unwrap-meta-name fields-vec)] + (var bi 0) + (while (< bi (length body)) + (def elem (in body bi)) + (if (and (struct? elem) (= :symbol (elem :jolt/type))) + (let [proto-sym elem + et @[{:jolt/type :symbol :ns nil :name "extend-type"} type-name proto-sym]] + (++ bi) + (while (and (< bi (length body)) + (not (and (struct? (in body bi)) (= :symbol ((in body bi) :jolt/type))))) + (let [spec (in body bi) + mname (in spec 0) + argv (in spec 1) + mbody (tuple/slice spec 2) + instance (in argv 0) + field-binds @[] + _ (each f field-syms + (array/push field-binds f) + (array/push field-binds @[{:jolt/type :symbol :ns nil :name "get"} + instance (keyword (f :name))])) + wrapped @[{:jolt/type :symbol :ns nil :name "let"} + (tuple/slice (tuple ;field-binds)) ;mbody]] + (array/push et @[mname argv wrapped])) + (++ bi)) + (eval-form ctx bindings et)) + (++ bi)))) (var-get (ns-intern ns ctor-name)))) "new" (let [type-sym (in form 1) args (map |(eval-form ctx bindings $) (tuple/slice form 2))