Merge pull request #38 from jolt-lang/stage3-ns-var

*ns*: the current-namespace dynamic var
This commit is contained in:
Dmitri Sotnikov 2026-06-10 13:05:22 -04:00 committed by GitHub
commit 6699ef47d3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 46 additions and 9 deletions

View file

@ -3,16 +3,16 @@
Generated 2026-06-10 by `tools/spec_coverage.py` — do not edit by hand. Generated 2026-06-10 by `tools/spec_coverage.py` — do not edit by hand.
Surface: **694** clojure.core vars (ClojureDocs export; 648 with Surface: **694** clojure.core vars (ClojureDocs export; 648 with
community examples). jolt interns 552 of them. community examples). jolt interns 553 of them.
| Status | Count | Meaning | | Status | Count | Meaning |
|---|---|---| |---|---|---|
| implemented+tested | 415 | in jolt and exercised by spec/conformance | | implemented+tested | 415 | in jolt and exercised by spec/conformance |
| implemented-untested | 137 | in jolt, no direct test — spec entries will add them | | implemented-untested | 138 | in jolt, no direct test — spec entries will add them |
| resolvable-not-interned | 2 | works in code but invisible to ns introspection (conformance finding) | | resolvable-not-interned | 2 | works in code but invisible to ns introspection (conformance finding) |
| missing-portable | 10 | portable semantics, jolt lacks it — implementation gap | | missing-portable | 10 | portable semantics, jolt lacks it — implementation gap |
| special-form | 15 | specified in §3, not a library var | | special-form | 15 | specified in §3, not a library var |
| dynamic-var | 31 | classification needed: portable default vs host-dependent | | dynamic-var | 30 | classification needed: portable default vs host-dependent |
| agents-taps | 22 | out of scope pending concurrency design note | | agents-taps | 22 | out of scope pending concurrency design note |
| stm-refs | 11 | out of scope pending concurrency design note | | stm-refs | 11 | out of scope pending concurrency design note |
| jvm-specific | 53 | catalogued, not specified | | jvm-specific | 53 | catalogued, not specified |
@ -47,7 +47,7 @@ UNVERIFIED field; that column will be added as entries land.
| `*fn-loader*` | dynamic-var | | | `*fn-loader*` | dynamic-var | |
| `*in*` | dynamic-var | | | `*in*` | dynamic-var | |
| `*math-context*` | dynamic-var | | | `*math-context*` | dynamic-var | |
| `*ns*` | dynamic-var | ✓ | | `*ns*` | implemented-untested | ✓ |
| `*out*` | dynamic-var | ✓ | | `*out*` | dynamic-var | ✓ |
| `*print-dup*` | dynamic-var | ✓ | | `*print-dup*` | dynamic-var | ✓ |
| `*print-length*` | dynamic-var | ✓ | | `*print-length*` | dynamic-var | ✓ |

View file

@ -1612,6 +1612,12 @@
# Readable rendering of a value (Clojure pr semantics): strings quoted, # Readable rendering of a value (Clojure pr semantics): strings quoted,
# keywords with leading ':', symbols by name, collections with their reader # keywords with leading ':', symbols by name, collections with their reader
# syntax. Used by both pr-str (readable) and str (collection elements). # syntax. Used by both pr-str (readable) and str (collection elements).
# A namespace's :name may be a string or a symbol struct depending on the
# creation path — normalize for display.
(defn- ns-display-name [ns]
(def n (ns :name))
(if (and (struct? n) (= :symbol (get n :jolt/type))) (n :name) (string n)))
(var pr-render nil) (var pr-render nil)
# Format a number the way Clojure prints it: infinity and NaN have named forms # Format a number the way Clojure prints it: infinity and NaN have named forms
@ -1686,6 +1692,10 @@
(and (struct? v) (= :jolt/inst (v :jolt/type))) (and (struct? v) (= :jolt/inst (v :jolt/type)))
(do (buffer/push-string buf "#inst \"") (buffer/push-string buf (inst->rfc3339 v)) (do (buffer/push-string buf "#inst \"") (buffer/push-string buf (inst->rfc3339 v))
(buffer/push-string buf "\"")) (buffer/push-string buf "\""))
(= :jolt/namespace (get v :jolt/type))
(do (buffer/push-string buf "#namespace[")
(buffer/push-string buf (ns-display-name v))
(buffer/push-string buf "]"))
(and (table? v) (= :jolt/var (get v :jolt/type))) (buffer/push-string buf (var-display v)) (and (table? v) (= :jolt/var (get v :jolt/type))) (buffer/push-string buf (var-display v))
(core-sorted-map? v) (pr-render-pairs buf (sorted-map-entries v)) (core-sorted-map? v) (pr-render-pairs buf (sorted-map-entries v))
(core-sorted-set? v) (pr-render-seq buf (v :items) "#{" "}") (core-sorted-set? v) (pr-render-seq buf (v :items) "#{" "}")
@ -1718,6 +1728,7 @@
(if (v :ns) (string (v :ns) "/" (v :name)) (v :name)) (if (v :ns) (string (v :ns) "/" (v :name)) (v :name))
(and (struct? v) (= :jolt/uuid (v :jolt/type))) (v :str) (and (struct? v) (= :jolt/uuid (v :jolt/type))) (v :str)
(and (struct? v) (= :jolt/inst (v :jolt/type))) (inst->rfc3339 v) (and (struct? v) (= :jolt/inst (v :jolt/type))) (inst->rfc3339 v)
(= :jolt/namespace (get v :jolt/type)) (ns-display-name v)
(and (table? v) (= :jolt/var (get v :jolt/type))) (var-display v) (and (table? v) (= :jolt/var (get v :jolt/type))) (var-display v)
(number? v) (fmt-number v) (number? v) (fmt-number v)
(= true v) "true" (= true v) "true"

View file

@ -796,9 +796,9 @@
quoted symbol arrives evaluated." quoted symbol arrives evaluated."
[ctx sym] [ctx sym]
(def ns-name (if (and (struct? sym) (= :symbol (sym :jolt/type))) (sym :name) (string sym))) (def ns-name (if (and (struct? sym) (= :symbol (sym :jolt/type))) (sym :name) (string sym)))
(ctx-find-ns ctx ns-name) (def the-ns-obj (ctx-find-ns ctx ns-name))
(ctx-set-current-ns ctx ns-name) (ctx-set-current-ns ctx ns-name)
nil) the-ns-obj)
(defn use-impl (defn use-impl
"(use '[ns ...] ...) — refer ALL public vars of each used ns into the CURRENT ns. "(use '[ns ...] ...) — refer ALL public vars of each used ns into the CURRENT ns.
@ -953,6 +953,12 @@
# Var/namespace lookups that need the ctx (the rest of the var fns — var-get/ # Var/namespace lookups that need the ctx (the rest of the var fns — var-get/
# var-set/var?/alter-var-root/alter-meta!/reset-meta! — are plain core-bindings). # var-set/var?/alter-var-root/alter-meta!/reset-meta! — are plain core-bindings).
(ns-intern core "find-var" (fn [sym] (find-var ctx sym))) (ns-intern core "find-var" (fn [sym] (find-var ctx sym)))
# *ns*: the current-namespace dynamic var. Its root is kept in sync by
# ctx-set-current-ns via the cached var table (env :ns-var); a thread
# binding (binding [*ns* ...]) shadows the root through var-get as usual.
(def ns-var (ns-intern core "*ns*" (ctx-find-ns ctx (ctx-current-ns ctx))))
(put ns-var :dynamic true)
(put (ctx :env) :ns-var ns-var)
(ns-intern core "intern" (ns-intern core "intern"
(fn [ns-name sym-name &opt val] (fn [ns-name sym-name &opt val]
(def ns (ctx-find-ns ctx (if (struct? ns-name) (ns-name :name) ns-name))) (def ns (ctx-find-ns ctx (if (struct? ns-name) (ns-name :name) ns-name)))
@ -969,7 +975,7 @@
(keyword? x) (string x) (keyword? x) (string x)
nil))) nil)))
(def ns-of (fn [x] (def ns-of (fn [x]
(if (and (table? x) (not (nil? (x :mappings)))) (if (= :jolt/namespace (get x :jolt/type))
x x
(let [nm (ns-name-of x)] (let [nm (ns-name-of x)]
(if nm (get (get (ctx :env) :namespaces) nm) nil))))) (if nm (get (get (ctx :env) :namespaces) nm) nil)))))

View file

@ -509,9 +509,13 @@
(get (ctx :env) :current-ns)) (get (ctx :env) :current-ns))
(defn ctx-set-current-ns (defn ctx-set-current-ns
"Set the current namespace symbol." "Set the current namespace symbol. Also keeps the *ns* dynamic var's root in
sync (the var table is cached on the env by install-stateful-fns! — one table
put on this hot path, no ns lookup chain)."
[ctx ns-sym] [ctx ns-sym]
(put (ctx :env) :current-ns ns-sym)) (put (ctx :env) :current-ns ns-sym)
(when-let [v (get (ctx :env) :ns-var)]
(put v :root (ctx-find-ns ctx ns-sym))))
(defn all-ns (defn all-ns
"Return a list of all namespaces in the context." "Return a list of all namespaces in the context."

View file

@ -0,0 +1,16 @@
# Specification: *ns* — the current-namespace dynamic var (stage 3).
# *ns* holds the current NAMESPACE OBJECT; (str *ns*) is its name; it tracks
# in-ns at the top level and works with the ns-introspection fns.
(use ../support/harness)
(defspec "*ns* / identity & printing"
["str of *ns*" "\"user\"" "(str *ns*)"]
["ns-name of *ns*" "(quote user)" "(ns-name *ns*)"]
["*ns* is find-ns" "true" "(= (ns-name *ns*) (ns-name (find-ns (quote user))))"]
["*ns* not a map" "false" "(map? *ns*)"]
["tracks in-ns" "\"jolt.test-ns-a\"" "(do (in-ns (quote jolt.test-ns-a)) (str *ns*))"]
["in-ns returns ns" "\"jolt.test-ns-b\"" "(str (in-ns (quote jolt.test-ns-b)))"]
["usable with ns fns" "true"
"(do (require (quote clojure.string)) (alias (quote nsv) (quote clojure.string)) (some? (get (ns-aliases *ns*) (quote nsv))))"]
["ns-unalias via *ns*" "true"
"(do (require (quote clojure.string)) (alias (quote nsw) (quote clojure.string)) (ns-unalias *ns* (quote nsw)) (nil? (get (ns-aliases *ns*) (quote nsw))))"])