clojure-dev

borkdude 2022-11-26T14:40:47.857159Z

Should the docstring of empty? change, since the advice to use (seq ...) as a replacement for (not (empty? ..)) no longer holds for counted? collections that aren't seqable in clojure 1.12? clj-kondo warns against (not (empty? ...)) because of the docstring in empty? . I think discussions about this could be ended once and for all if core has a not-empty? function. I know there is not-empty as well but this isn't updated to do what empty? in clojure 1.12 does and doesn't return a boolean which is what some people want (https://twitter.com/ryrobes/status/1595923346700599298).

Alex Miller (Clojure team) 2022-11-26T14:47:05.761759Z

it did change slightly, but seq is still the preferred idiom in seq contexts (which is how the docstring now reads) http://clojure.github.io/clojure/branch-master/clojure.core-api.html#clojure.core/empty?

borkdude 2022-11-26T14:47:51.570289Z

makes sense

Alex Miller (Clojure team) 2022-11-26T14:52:19.815389Z

perhaps the linter could be more nuanced in this case, and only apply if x is a seq (and not a seqable)?

borkdude 2022-11-26T14:54:21.081799Z

yeah

borkdude 2022-11-26T14:54:50.605529Z

hm?

borkdude 2022-11-26T14:55:22.978439Z

I didn't understand the "and not a seqable" part

Alex Miller (Clojure team) 2022-11-26T15:00:39.811769Z

like if x is a vector, then it is seqable, but it is not a seq

Alex Miller (Clojure team) 2022-11-26T15:01:07.557989Z

so in the case of (not (empty? a-vector)), maybe this warning no longer applies

Alex Miller (Clojure team) 2022-11-26T15:01:23.811259Z

but if you have (not (empty? (filter ...))) it would

borkdude 2022-11-26T15:02:34.396229Z

I have the suspicion that most people wouldn't really care about this

borkdude 2022-11-26T15:03:22.942509Z

this is why I think a built-in not-empty? that did the right thing always would help

Alex Miller (Clojure team) 2022-11-26T15:08:35.446839Z

I would still say you should use seq in a seq context, even if not-empty? existed

Alex Miller (Clojure team) 2022-11-26T15:09:09.295649Z

the "right thing" is contextual and depends on semantics

borkdude 2022-11-26T15:09:27.226039Z

the right thing when you just want a boolean check (which the ? indicates)

borkdude 2022-11-26T15:12:24.146099Z

I'll try to make the linter more sophisticated. it could warn only when the argument is already a seq (and not a counted)

borkdude 2022-11-26T15:12:40.463139Z

so the warning here would disappear: (not (empty? []))

borkdude 2022-11-26T15:13:54.740769Z

but I still have a hard time convincing people running into this about the importance of it 😅

borkdude 2022-11-26T15:17:43.156849Z

I made an issue for it here earlier: https://github.com/clj-kondo/clj-kondo/issues/1743

borkdude 2022-11-26T15:19:04.289269Z

Is the issue with (not (empty? already-a-seq)) that seq is being called twice?

Alex Miller (Clojure team) 2022-11-26T15:19:46.763389Z

isn't (seq already-a-seq) just obviously better in being more concise?

borkdude 2022-11-26T15:20:36.769639Z

I have experienced that people don't find this obviously better - e.g. read the tweet I linked in the original post

Alex Miller (Clojure team) 2022-11-26T15:20:59.093609Z

that person is wrong /shrug

Alex Miller (Clojure team) 2022-11-26T15:21:35.249659Z

Clojure embraces nil punning and logical truth values for seqs

1
borkdude 2022-11-26T15:21:57.668019Z

the clojure shibboleth :)

Alex Miller (Clojure team) 2022-11-26T15:25:05.881689Z

I'm assuming that tweet is referring to code where this is a conditional pred, and imho seq is absolutely the right thing to call in that circumstance both aesthetically and for performance (assuming x is a seq)

borkdude 2022-11-26T15:26:50.674209Z

well, since it's in the docstring, I'll leave it in clj-kondo. I'm a happy seq user but even in core libraries I've seen usages of (not (empty? ...))

Alex Miller (Clojure team) 2022-11-26T15:27:12.167459Z

it has always bugged me (pre 1.12) to call seq in this circumstance when x is not already a seq. doing seq coercion is not necessarily fast for an arbitrary coll

Alex Miller (Clojure team) 2022-11-26T15:27:39.193149Z

and those are cases where I would call (not (empty? )) or (= 0 (count x))

borkdude 2022-11-26T15:28:03.983019Z

or (zero? (count x))?

Alex Miller (Clojure team) 2022-11-26T15:28:09.180499Z

yeah

Alex Miller (Clojure team) 2022-11-26T15:28:18.247339Z

zero? is actually optimized so that's better

borkdude 2022-11-26T15:28:24.939759Z

hmyeah, so it's a bit nuanced

Alex Miller (Clojure team) 2022-11-26T15:28:34.057799Z

or I would use empty? and flip the cases

Alex Miller (Clojure team) 2022-11-26T15:29:15.319069Z

I think empty? is semantically the cleanest thing to say for a collection

borkdude 2022-11-26T15:29:51.284769Z

(if (empty? ...) x y)

favila 2022-11-26T20:14:50.498469Z

I think what people want is “empty?/not-empty” to be like “conj”—just do the fastest thing for this type. The “use the (seq x) idiom” has always bothered me precisely because it is for seqs and so can never have polymorphic fast paths for empty checking like ”empty?” can.

👍 5
Alex Miller (Clojure team) 2022-11-26T20:24:35.151269Z

I don't want that

Alex Miller (Clojure team) 2022-11-26T20:25:45.992059Z

more specifically, I don't want to lose seq idioms. I do want to enable efficient collection without coercion to seq when you know that's what you have

Alex Miller (Clojure team) 2022-11-26T20:26:35.768679Z

empty? now takes advantage of faster paths for counted colls when it can

favila 2022-11-26T20:27:18.496409Z

That’s great, but people took the wrong lesson from the previous docstring

favila 2022-11-26T20:27:42.162069Z

Using seq for empty check is like using cons for conj

Alex Miller (Clojure team) 2022-11-26T20:28:58.864559Z

I'm not sure at this point whether you are satisfied with the 1.12 endpoint

favila 2022-11-26T20:32:56.650229Z

I am. I’m just frustrated this didn’t happen sooner and the (seq x) reflex has to be unlearned

favila 2022-11-26T20:34:05.998859Z

I do wonder if empty? should mention seq at all; or if seq should say “prefer empty? unless you know you have a seq”

favila 2022-11-26T20:35:05.735519Z

Remember this convo started with a linter rule scolding someone for using empty?

borkdude 2022-11-26T20:35:42.303919Z

well, we didn't know if that person was already using 1.12 or if their input was already a seq :)

borkdude 2022-11-26T20:36:04.245839Z

but I will change clj-kondo so it will only "complain" if the input is surely a seq

borkdude 2022-11-26T20:36:57.862879Z

but even then using (not (empty? ...)) on a seq isn't the end of the world. calling seq on an already-seq is cheap, I assume

favila 2022-11-26T20:37:00.931329Z

Not blaming you at all @borkdude :)

borkdude 2022-11-26T20:37:12.582449Z

so perhaps I should disable the linter altogether and make it opt-in

borkdude 2022-11-26T20:38:05.442619Z

well, it's not about blame, but about making clojure a better experience, let's focus on that :)

favila 2022-11-26T20:40:04.231069Z

But you’re picking up on community idioms that (imo) were already too scolding, and they emerged because empty? actually wasn’t any different till now and the docstring was really pushing you away from it

favila 2022-11-26T20:41:44.493099Z

Anyway I’m very happy about this change. I had a cempty? function I copied around that I will no longer need

favila 2022-11-26T20:42:31.914159Z

“Counted empty”