jolt/IMPLEMENTATION_PLAN.md
Yogthos 3ac293c7a5 Phase 0: Fix defn bug + bare symbol resolution
Root cause: compile-and-eval for def created Janet global but never
interned the var in Jolt's namespace. Bare symbols fell through to
interpreter which couldn't find them.

Fixes:
- eval-string: bare symbols and tuples now go through compile path
- compile-and-eval: def/defn/defn- forms intern result in Jolt namespace
- New tests: defn/def integration (4 assertions)

All 317 tests pass.
2026-06-02 16:58:10 -04:00

211 lines
6.1 KiB
Markdown

# Full Jolt Implementation Plan
## Strategy
Minimal Janet bootstrap (parser + var system + namespace system + core types)
→ load SCI's Clojure source → rest of stdlib is Clojure.
Jolt already has 3 phases matching SCI's architecture:
- Parser: reader.janet (hand-rolled, works)
- Analyzer: compiler.janet (2-phase: analyze-form → emit-expr)
- Evaluator: evaluator.janet (interpreter) + Janet's native eval (compiled path)
## Phase 0: Fix defn Bug + Symbol Resolution (now)
### 0.1: Bare symbols must go through compile path too
In api.janet eval-string, change:
(if (and compile? (array? form)) → also true for bare symbols
Only skip compiler for literal values (nil, numbers, strings, keywords, booleans).
Everything else → compile-and-eval.
### 0.2: compile-and-eval for def must intern in Jolt namespace
After (eval (compile-ast form ctx)), if the form is a def:
(ns-intern ns (name-sym :name) val)
So the interpreter can find the var later.
### 0.3: Add defn to Phase 6 tests
Test: (defn foo [x] x) → (foo 1) = 1, foo returns the fn
## Phase 1: Complete Var/Namespace System
### 1.1: Full Var semantics
- Var with dynamic bindings (push-thread-bindings/pop-thread-bindings)
- binding macro (already exists in evaluator, needs compiler support)
- alter-var-root, var-get, var-set
- ^:dynamic metadata
- ^:macro metadata (already there)
- Var metadata (already there)
### 1.2: Full Namespace system
- ns form with :require, :use, :import, :refer-clojure, :refer
- create-ns, find-ns, all-ns, remove-ns
- ns-aliases, ns-refers, ns-imports
- ns-name, ns-map, ns-interns
- in-ns (already there)
### 1.3: Interop symbol resolution
- clojure.core/* for fully qualified
- ns-alias/name for aliased
- Import resolution (ClassName → constructor)
Tests: Port SCI's var_test.cljs and ns_test.cljs
## Phase 2: Complete Core Library
### 2.1: PersistentHashMap
Port from CLJS (cljs.core/PersistentHashMap) using hash-array-mapped trie.
Current Janet struct fallback is O(n). Need:
- Array-based HAMT with 32-way branching
- hash and = consistent with Clojure semantics
- transient support for bulk operations
### 2.2: PersistentHashSet
Port from CLJS. Wrap PersistentHashMap with sentinel values.
### 2.3: Lazy Sequences
- LazySeq type
- lazy-seq macro
- Realize-once semantics
- Sequence caching (already computed? check)
### 2.4: Remaining clojure.core fns (ported from SCI)
- clojure.string (port from CLJS)
- clojure.set (port from CLJS)
- Sequence library: partition, split-at, split-with, interleave, interpose
- Reducers: reduce-kv, keep, keep-indexed
- sorted maps/sets (skip for now — use hash-based)
- chunked sequences (skip for now)
## Phase 3: Protocol System
### 3.1: defprotocol
Port from SCI. Protocols are maps of method-name → dispatch-fn.
Protocol methods compile to calls through the protocol dispatch table.
### 3.2: extend-type / extend-protocol
Type → protocol method implementations.
Stored in type metadata.
### 3.3: reify
Anonymous type implementing protocols.
### 3.4: satisfies? / extends?
Runtime protocol check.
Tests: Port SCI's protocol tests
## Phase 4: deftype / defrecord (Complete)
### 4.1: deftype with mutable fields
- ^:volatile-mutable / ^:unsynchronized-mutable
- Field accessors (.field obj)
- Field mutation (set! (.-field obj) val)
### 4.2: defrecord
- Creates type + factories (→RecordName, map->RecordName)
- IPersistentMap implementation
- equals/hashCode based on type + fields
## Phase 5: Multimethods
### 5.1: Hierarchy
- derive, isa?, ancestors, descendants
- make-hierarchy
- Global hierarchy atom
### 5.2: defmulti / defmethod
- Dispatch function
- Method dispatch via hierarchy
- :default dispatch value
- prefer-method
## Phase 6: Reader Extensions
### 6.1: Tagged Literals
- #inst, #uuid
- default-data-readers
- *data-readers* dynamic var
### 6.2: Reader Conditionals
- #? and #?@ for platform-specific code
- Feature expressions (:clj, :cljs, :jolt)
### 6.3: Metadata reader
- ^:key val and ^{:key val} forms
- Attach metadata to any form
## Phase 7: SCI Bootstrap
### 7.1: Load SCI source
- Parse and load sci.core, sci.lang, sci.impl.* namespaces
- Expose Jolt runtime as Clojure vars (types, vars, namespaces)
- SCI's Clojure code runs on Jolt's eval stack
### 7.2: Benchmark
- Compare Jolt (Janet) vs Jolt+SCI (Clojure-on-Jolt)
- Use SCI's existing benchmark suite
- Identify bottlenecks for optimization
## Phase 8: Compiler Optimization
### 8.1: Better symbol resolution
- Known locals → direct Janet var access
- Known core fns → direct fn invocation (already done)
- Var deref → inline deref
### 8.2: Loop optimization
- Primitive loop/recur (already done with closure-based)
- Tail-call optimization for self-recursion
### 8.3: Inlining
- Small core functions (inc, dec, +, -) inlined
- Type hints for numeric ops
## Phase 9: Integration Testing
### 9.1: Port CLJS test suite
- collections_test.cljs → Jolt
- core_test.cljs → Jolt
- seqs_test.cljs → Jolt
- predicates_test.cljs → Jolt
- recur_test.cljs → Jolt
### 9.2: Self-hosting test
- Jolt running Jolt's own test suite
- Jolt running SCI's test suite
## Phase 10: Standard Library (Clojure-written)
### 10.1: Port from CLJS/SCI
- clojure.string
- clojure.set
- clojure.walk
- clojure.zip
- clojure.edn (reader already, need writer)
- clojure.java.io (Janet file I/O)
### 10.2: Jolt-specific
- jolt.interop (Janet interop)
- jolt.shell (Janet shell commands)
- jolt.http (Janet HTTP client)
## Implementation Order
1. Phase 0 (defn bug) — **now**
2. Phase 1 (var/ns system) — **this week**
3. Phase 2 (core library) — **next week**
4. Phase 6 (reader extensions) — allows loading SCI directly
5. Phase 7 (SCI bootstrap) — critical path
6. Phases 3-5 (protocols, deftype, multimethods) — as needed by SCI
7. Phases 8-10 (optimization, testing, stdlib) — polish
## Testing Strategy
Each phase: write tests FIRST (TDD), then implement.
Test files follow: test/<feature>_test.janet
- Literal/source tests: (assert (= "expected" (compile-str "input")) "label")
- Round-trip tests: (assert (= val (ct-eval ctx "input")) "label")
- Integration tests: load .clj files, eval, verify namespaces/vars
Run: janet test/<feature>_test.janet (single) or jpm test (all)