clojure 2026-01-08

Has anyone had problems with ring-defaults 0.7.0? I am using

ring/ring-defaults {:mvn/version "0.7.0", :exclusions [ring/ring-core]},
ring/ring-jetty-adapter {:mvn/version "1.15.3"},
and reitit and this has worked in version 0.6.0 but when I upgrade ring-defaults to 0.7.0 suddenly all the HTML pages are only returned half. Like it literally cuts the response in the middle.

Content-Length header seems to match this shortened document

I see, middleware 0.6.0 doesn’t return Content-Length header at all but middleware 0.7.0 does and it is off by a number of bytes so jetty just cuts the response there

The problem is that Content-Length no longer matches after another middleware adds a header after the content length middleware has already calculated the content length

unfortunately https://github.com/ring-clojure/ring/blob/master/SPEC.md is not clear on casing of headers and I frequently run into this

I guess this could be a Clojure brain teaser (I mistyped it as teasure first, which is close enough to seizure...)

'(fn [x] `(dude ~x))
Wtx does ~x here? Turns out, it just expands to x because of the outside quote

Oh this is so fun 😆

Imma try a dozen more permutations now.

Wouldn't it expand to (unquote x)?

user=> '(fn [x] `(dude ~x))
(fn [x] (clojure.core/seq (clojure.core/concat (clojure.core/list (quote user/dude)) (clojure.core/list x))))

Ohh, it expands to (concat (list (quote dude)) (list x)), I imagined it would work like in common lisp. Well, the result is the same 😁. AFAIK, in common lisp we would get something like:

(fn [x] (backquote (dude (unquote x))))
I think it would be the same in Scheme.

Ohh, I just discovered that (unquote) and (unquote-splicing) "doesn't exist" in Clojure, we have just ~ and ~@. I would never notice that if this thread didn't exist 😁, definitely a brain teaser hehehe.

they do exist:

user=> (let [x 1] `(clojure.core/unquote x))
1
user=> (let [x 1] `((clojure.core/unquote-splicing [x])))
(1)

but they are treated specially by the reader

Ahh, that make sense

Thank you!

Hi all, is it still the case that (seq x) is the preferred way to check if something is not empty as opposed to (not (empty? x))? Since 2022, empty? has an additional optimisation for counted collections in that it calls .count instead of creating a seq, making it significantly faster. On my machine, (not (empty? x)) is 3x faster on vectors for example than (boolean (seq x)). Is this just a case where the docstring needs updating?

If the performance doesn't matter then why does empty? check if the collection is counted and use .count? I don’t see why we only care about perf for empty and not for not empty. Obviously it's all workflow dependent but I’ve found the performance does matter especially when it's being called in say a nested loop. I’ve gotten double digit % performance increases more than once just by calling seq fewer times (and I notice clojure.core makes similar efforts, so it can’t just be me). Using my own judgement is all well and good but in this case the docstring specifically says not to do it and so going against that is a tough sell during code review. I guess I'd just like to know the justification for preferring (seq x), and why it's important enough to call it out in the docstring

I have two mindsets: in my libs, particularly the lower-level ones, I micro-optimize things like this, because I want to leave as much of the GC and runtime budget as possible for the user of the lib. In app code, I don’t, because it’s vastly overshadowed by other things and doesn’t matter so I might use one or the other.

💯 1

Yeah I work mostly on lower level, performance critical code at our company, e.g. database query engines. I agree that in higher level business logic stuff like this doesn’t tend to matter so much, and it’s also something I wouldn’t touch until a benchmark shows it as being a bottleneck, but IME it does tend to eventually become important at some point in the stack. Oh! I just realised that instructions in the empty? docstring (`"To check the emptiness of a seq, please use the idiom (seq x) rather than (not (empty? x))"`) specifically say of a seq. Could one interpret that as meaning that for non-seqs (e.g. vectors) (not (empty? x)) is ok? Would it make sense to soften the language slightly? Like could we add “unless you know what you are doing” at the end or something? Pre-2022, (empty? x) was just (not (seq x)) so (not (empty? x)) was clearly a bad idea. But I don’t think it’s so cut and dry any more now that the functions have different internal behaviour, so having the language tell you not to do it seems a bit strong and it makes me a little uncomfortable shipping code that goes against it

If you know that you have a vector, (java.util.Collection/.isEmpty v) might be your fastest option.

I find (not (empty? x)) way WAY more readable than seq (which means near nothing to a new joiner). I wasn't aware of the performance diff, but knowing that I'll actively discourage people from using seq for that purpose

Come to think of it, maybe we can trim it to a pointer comparison:

(identical? [] PersistentVector/EMPTY)                      ; true
(identical? (vector) PersistentVector/EMPTY)                ; true
(identical? (pop [1]) PersistentVector/EMPTY)               ; true
(identical? (subvec [1 2 3] 1 1) PersistentVector/EMPTY)    ; true
(identical? (into [] nil) PersistentVector/EMPTY)           ; false
(identical? (vec nil) PersistentVector/EMPTY)               ; false
(identical? (persistent! (transient [])) PersistentVector/EMPTY) ; false
(identical? (vec (list)) PersistentVector/EMPTY)            ; false
No, not quite.

optimization potential spotted.

Golf trophy achieved

😍 1

I'll also agree with henrik here; the code I write for my libraries generally focuses on micro-optimizations because libraries generally don't want to make too many choices for the user unless they are opinionated and say as much. Being poorly optimized is making a choice for the user; that choice being that the library is inappropriate in performance-sensitive contexts.

👍 1

Please don't use (if (not (empty? x)) .. ..) Prefer (if-not (empty? x) .. ..) or (when-not (empty? x) ..) 😉

Alex Miller (Clojure team) 2026-01-10T18:22:32.802589Z

(I'll repeat Java 25 often surprises me, might be more useful than 21.) Refining my advice at the top, if you are in code where you know you have a collection, then I would use empty?. But if you are in a seq context, I would still use seq (and the optimization is less likely to matter then anyways)

👍 1

Here’s a highly scientific complementary measurement of vec-empty? . 1.25ns or ~25% faster than empty?! Imagine that you have to check the emptiness of 1 trillion vectors, and corporate has decided that you have to listen to Baby Shark on a loop while doing so. You’d cut it down from 88 minutes to 66 minutes of Baby Shark only.

👍 1
🦈 1

(Measured on JDK 25)

Interesting, so most of the difference must come from the PersistentVector/EMPTY then, which makes sense I guess since a pointer comparison is ~free.

My benchmark setup is not as sophisticated as yours @hugod, but I wanted to see how good the JDK is at eliding the indirections and polymorphism etc. This is what I got: Clojure core: 6,575288 ns Custom (clojure): 5,144892 ns Custom (java): 2,982085 ns This was with an empty and a populated vector in each benchmark.

Alex Miller (Clojure team) 2026-01-08T20:54:11.318649Z

seq is still the preferred idiom. I am doubtful that the performance difference is important in the vast majority of cases

➕ 1

In cases where performance is a strong consideration, most Seq implementations are fairly simple small objects constructed with references to the target collection and that allocation is pretty insignificant, and both G1 and ZGC are very good at small ephemeral allocations.

Alex Miller (Clojure team) 2026-01-08T22:08:52.604039Z

I think if you benchmark the two calls in the context of doing some real work (where the actual work dominates), this is usually irrelevant. things like this are also well optimized by the JVM. I have been particularly impressed with Java 25 lately - it really is just ridiculously good vs 21 or older.

I still use (not (empty? x)) or empty? with reversed clauses because it's more readable. use your own judgement.

➕ 5

It would be fantastic if source could be used on functions that use auto-resolved keywords ::ns/name - it throws an exception. Auto-resolved keywords are very common in the code and libraries I work with

Upvotes matter. The core team looks at them. If you want it, upvote it.

Upvoted, thanks!

1

ah I found a ticket for this https://clojure.atlassian.net/browse/CLJ-2359 from 7 years ago!