Fix clojure.walk to descend lists and seqs (jolt-khk)

walk only handled vector?/map? and fell through :else for everything else, so
postwalk over a quoted list (a plist) never touched its elements —
postwalk-replace with symbol keys silently no-op'd, which broke
clojure.template/apply-template (found during reitit work). Add list? (rebuild as
a list) and seq? (map over it) branches after the vector/map ones so concrete
collections stay authoritative. Adds walk-spec covering list/seq walking plus a
vector/keywordize regression guard and the apply-template trigger.
This commit is contained in:
Yogthos 2026-06-15 10:16:47 -04:00
parent 1ffb348c5e
commit fb1ec25c69
2 changed files with 30 additions and 1 deletions

View file

@ -1,12 +1,18 @@
; Jolt Standard Library: clojure.walk
; Tree walking for Clojure data structures.
; Simplified: uses vector? and map? predicates (no list? or seq?).
(defn walk
[inner outer form]
(cond
; vectors/maps first so seq? can't swallow them (a vector is not seq? on
; jolt, but keep the concrete branches authoritative)
(vector? form) (outer (vec (map inner form)))
(map? form) (outer (into (empty form) (map inner form)))
; lists rebuild as lists, other seqs (incl. macro/template output: cons/
; concat/lazy-seq) walk too — without this, postwalk-replace silently no-op'd
; a quoted list, breaking clojure.template/apply-template (jolt-khk)
(list? form) (outer (apply list (map inner form)))
(seq? form) (outer (map inner form))
:else (outer form)))
(defn postwalk

23
test/spec/walk-spec.janet Normal file
View file

@ -0,0 +1,23 @@
# Specification: clojure.walk must descend lists and seqs, not just vectors/maps
# (jolt-khk). postwalk-replace with symbol keys over a quoted list silently
# no-op'd because walk only handled vector?/map? and fell through for list?/seq?
# — which broke clojure.template/apply-template (found during reitit work).
(use ../support/harness)
(defspec "clojure.walk / lists + seqs"
["postwalk-replace symbol keys in a list" "(quote (+ 2 2))"
"(do (require (quote [clojure.walk :as w])) (w/postwalk-replace {(quote x) 2} (quote (+ x x))))"]
["postwalk descends a list" "(quote (:a :a))"
"(do (require (quote [clojure.walk :as w])) (w/postwalk (fn [n] (if (symbol? n) :a n)) (quote (x y))))"]
["prewalk-replace in a list" "(quote (* 3 3))"
"(do (require (quote [clojure.walk :as w])) (w/prewalk-replace {(quote *) (quote *) (quote y) 3} (quote (* y y))))"]
["nested list + vector" "(quote (1 [2 1]))"
"(do (require (quote [clojure.walk :as w])) (w/postwalk-replace {:a 1 :b 2} (quote (:a [:b :a]))))"]
# vectors/maps still work (regression guard for the existing behavior)
["postwalk-replace in a vector" "[:one 2 :one]"
"(do (require (quote [clojure.walk :as w])) (w/postwalk-replace {1 :one} [1 2 1]))"]
["keywordize-keys still works" "{:a 1}"
"(do (require (quote [clojure.walk :as w])) (w/keywordize-keys {\"a\" 1}))"]
# clojure.template/apply-template (the real-world trigger) substitutes now
["apply-template substitutes" "(quote (+ 1 2))"
"(do (require (quote [clojure.template :as t])) (t/apply-template (quote [x y]) (quote (+ x y)) (quote (1 2))))"])