clj-kondo

dharrigan 2026-01-18T09:05:22.964759Z

Might a linter that flags usage of (if (contains? #{:foo :bar} :baz) :a :b) could be rewritten as (if (#{:foo :bar} :baz) :a :b) be something to maybe consider?

borkdude 2026-01-18T09:50:21.012929Z

I actually like contains? but this is because very early on in my first production use of Clojure a colleague told me he preferred it. I've been doing this since this day, even I know the latter alternative 😆

borkdude 2026-01-18T09:50:39.466899Z

Are there any perf differences between the two?

dharrigan 2026-01-18T09:51:32.923169Z

I only recently learnt about it too (after reading some other Clojure code). I don't know if there is a performance difference.

borkdude 2026-01-18T09:54:54.879659Z

I mean, I suggested (#{:foo :bar} :foo) to my colleague in 2013, but he preferred contains? since it was more explicit

borkdude 2026-01-18T09:55:56.160909Z

I guess the difference is that the IFn call is the get equivalent

(#{:foo :bar} :foo)
:foo
and it doesn't always return a boolean

borkdude 2026-01-18T09:56:09.756389Z

in CLJS this can be mean a small perf difference since you need a truth check

dharrigan 2026-01-18T09:56:28.880529Z

I see. I'm not much at all in the CLJS world.

dharrigan 2026-01-18T09:57:03.242109Z

I have been writing contains? too

borkdude 2026-01-18T09:57:21.355009Z

cljs.user=> (str (fn [] (if (#{0 1} 0) true false)))
"function (){\nif(cljs.core.truth_(new cljs.core.PersistentHashSet(null, new cljs.core.PersistentArrayMap(null, 2, [(0),null,(1),null], null), null).call(null,(0)))){\nreturn true;\n} else {\nreturn false;\n}\n}"
cljs.user=> (str (fn [] (if (contains? #{0 1} 0) true false)))
"function (){\nif(cljs.core.contains_QMARK_.call(null,new cljs.core.PersistentHashSet(null, new cljs.core.PersistentArrayMap(null, 2, [(0),null,(1),null], null), null),(0))){\nreturn true;\n} else {\nreturn false;\n}\n}"

borkdude 2026-01-18T09:57:40.475239Z

the truth check in the latter can be skipped since contains? always returns a boolean

dharrigan 2026-01-18T09:58:48.242169Z

Perhaps best then, to leave it. 🙂

borkdude 2026-01-18T10:00:56.415779Z

There also seems to be an odd performance difference. I expected contains? to be faster in CLJS, but no:

cljs.user=> (time (let [x #{:a :b :c}] (dotimes [i 100000000] (if (contains? x :a) true false))))
"Elapsed time: 2829.361250 msecs"
nil
cljs.user=> (time (let [x #{:a :b :c}] (dotimes [i 100000000] (if (x :a) true false))))
"Elapsed time: 1504.274500 msecs"
nil

borkdude 2026-01-18T10:01:23.301199Z

I guess because of some protocolesque call

borkdude 2026-01-18T10:01:44.712749Z

anyway, it's not the same equivalent, so perhaps best to leave it

👍 1
dominicm 2026-01-18T13:00:59.508399Z

Nil is a fun related case too

👍 2
borkdude 2026-01-18T13:02:05.982319Z

and false