beginners 2025-06-18

Hey all! Does anyone know of something like clojure.walk/prewalk that allows for halting further traversal at an arbitrary depth? ie. I want to return a large highly nested data structure and I don't want to force further walking

👍 1

You can always throw :)

➕ 1

Wouldn't that halt the traversal of peers though too, not just children?

I think zippers are great for this kind of stuff!

specter also has support for this kind of thing

iirc prewalk is based on reduce so maybe it will already react to ‘reduced’

if you look at the source, prewalk is very small and based on walk, also a small function. you could modify to track depth and short circuit

Nice, good tips! Thanks all

ins)user=> (source clojure.walk/prewalk)
(defn prewalk
  "Like postwalk, but does pre-order traversal."
  {:added "1.1"}
  [f form]
  (walk (partial prewalk f) identity (f form)))
nil
(ins)user=> (source clojure.walk/walk)
(defn walk
  "Traverses form, an arbitrary data structure.  inner and outer are
  functions.  Applies inner to each element of form, building up a
  data structure of the same type, then applies outer to the result.
  Recognizes all Clojure data structures. Consumes seqs as with doall."

  {:added "1.1"}
  [inner outer form]
  (cond
   (list? form) (outer (with-meta (apply list (map inner form)) (meta form)))
   (instance? clojure.lang.IMapEntry form)
   (outer (clojure.lang.MapEntry/create (inner (key form)) (inner (val form))))
   (seq? form) (outer (with-meta (doall (map inner form)) (meta form)))
   (instance? clojure.lang.IRecord form)
     (outer (reduce (fn [r x] (conj r (inner x))) form form))
   (coll? form) (outer (into (empty form) (map inner form)))
   :else (outer form)))
nil

Something like:

(defn prewalk-upto
  ([max-depth f form]
   (prewalk-upto max-depth f 0 form))
  ([max-depth f depth form] 
   (if (= max-depth depth)
     form
     (let [depth' (if (map? form) depth (inc depth))]
       (walk/walk (partial prewalk-upto max-depth f depth') identity (f depth form))))))
? The check for the form being a map is to avoid a two-level jump in depth, which likely isn't what you want anyway, alternatively you could shift it over to map-entry? or remove it

at first glance that looks like a good start

(But assume I need the prewalk for other reasons and can't just fallback to postwalk)

Hi all, massive noob here so apologies for such a basic question, but why does (.toUpperCase 3.0) not error out in the repl?

Are you perchance in a ClojureScript REPL?

Could it be a JavaScript coercion thing?

it's happening in the calva repl, on the jvm

I wonder if it's a calva feature, to make repl stuff easier

Sounds more like a Calva bug, hiding errors.

Whats does (+ 1 "hello") produce?

In a "blank" REPL I get the expected error:

$ clj
Clojure 1.12.0
user=> (.toUpperCase 3.0)
Execution error (IllegalArgumentException) at user/eval1 (REPL:1).
No matching field found: toUpperCase for class java.lang.Double
user=>

(`clj` on PATH, no related file in current folder - no clj file, no deps.edn)

I can't think of what could cause this kind of behaviour, besides you explicitly redefining .toUpperCase. Can you share more of the context? Ideally the entire context in which you're running that REPL.

Can you confirm that, on your machine, it works as expected if you run clj outside your project? e.g.

cd $(mktemp -d)
clj
(.toUpperCase 3.0)

hmmmm that one fails properly

not sure if there's some missing context or something, but it does seem to error in the REPL:

> clj
Clojure 1.12.1
user=> (.toUpperCase 3.0)
Execution error (IllegalArgumentException) at user/eval1 (REPL:1).
No matching field found: toUpperCase for class java.lang.Double

Are you running this in a fresh REPL? Does your REPL have any dependencies loaded or anything?