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 ; Jolt Standard Library: clojure.walk
; Tree walking for Clojure data structures. ; Tree walking for Clojure data structures.
; Simplified: uses vector? and map? predicates (no list? or seq?).
(defn walk (defn walk
[inner outer form] [inner outer form]
(cond (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))) (vector? form) (outer (vec (map inner form)))
(map? form) (outer (into (empty form) (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))) :else (outer form)))
(defn postwalk (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))))"])