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 quoteOh 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.
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.
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.
Please don't use (if (not (empty? x)) .. ..)
Prefer (if-not (empty? x) .. ..) or (when-not (empty? x) ..) 😉
Details of the differences in execution times: http://hugoduncan.org/clj-lang-benchmarks/not_empty.html
(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)
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.
(Measured on JDK 25)
Updated with JDK 25 and Collection/.isEmpty http://hugoduncan.org/clj-lang-benchmarks/not_empty.html Clay notebook source: https://github.com/hugoduncan/clj-lang-benchmarks/blob/master/notebooks/not_empty.clj
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.
seq is still the preferred idiom. I am doubtful that the performance difference is important in the vast majority of cases
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.
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.
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
Might be worth upvoting on Ask: https://ask.clojure.org/index.php/3371/clojure-repl-source-chokes-when-reading-code-with-resolved
Upvotes matter. The core team looks at them. If you want it, upvote it.
ah I found a ticket for this https://clojure.atlassian.net/browse/CLJ-2359 from 7 years ago!