clojure-dev

borkdude 2025-12-06T15:21:07.298889Z

Any reason only a map implements IFn with an extra not found value?

user=> ({:a 1} :b :b)
:b
user=> (#{:a :b :c} :d :d)
Execution error (ArityException) at user/eval29 (REPL:1).
Wrong number of args (2) passed to: clojure.lang.PersistentHashSet
user=> ([] 0 :d)
Execution error (ArityException) at user/eval31 (REPL:1).
Wrong number of args (2) passed to: clojure.lang.PersistentVector

borkdude 2025-12-06T15:21:57.181459Z

Not really a problem, just something I wondered about when implementing invalid arity linting for collections

dpsutton 2025-12-06T15:25:43.339949Z

The set ifn is contains right? Feels weird for that to have a not found notion. And vectors throw index out of bounds exceptions. To me it’s only a map that may or may not have a value associated with something, right?

dpsutton 2025-12-06T15:26:08.192669Z

but now i’m reading nth docstring

borkdude 2025-12-06T15:26:57.544839Z

it's not contains? for set:

user=> (#{:foo :bar} :foo)
:foo 

dpsutton 2025-12-06T15:26:57.894519Z

You convinced me on the vector, less so on the set.

borkdude 2025-12-06T15:27:21.997359Z

this would be a good use case for set:

(#{:foo nil} nil :not-found)

borkdude 2025-12-06T15:28:02.103469Z

I agree you can do this in 10 different other ways in clojure, it just feels inconsistent to treat maps and sets differently. sets are basically maps that map keys onto themselves

👍 1
➕ 2
borkdude 2025-12-06T15:29:38.652129Z

The thing I'm trying to catch in clj-kondo is this:

(map {:a :b} [1 2 3] [1 2 3])
but this was actually valid, which I didn't know from intuition. Then I tested it for other collections.

dpsutton 2025-12-06T15:30:38.490749Z

go is infuriating to me because that’s actually canonically how they do sets. maps with an empty struct as a value. But i always consider that one implementation detail of sets, not necessarily an abstract definition of them. That example does look strange but makes sense.

borkdude 2025-12-06T15:30:58.868789Z

$ clj -M:clj-kondo/dev --lint - <<< "(map {:a :b} [1 2 3] [1 2 3] [1 2 3])"
<stdin>:1:6: error: a map is called with 3 args but expects 1 or 2
linting took 29ms, errors: 1, warnings: 0

borkdude 2025-12-06T15:31:47.835789Z

I don't think it's just an implementation detail, it's a mathematical notion too

dpsutton 2025-12-06T15:33:34.405769Z

i’m cracking up because the name of the set is impl

dpsutton 2025-12-06T15:34:40.079329Z

but you certainly don’t “assoc set value true”. you “add” an element to a set. You can model it as a map associating that value to some sentinel value. I think javascript does this for arrays as well. But it’s an implementation detail to me.

borkdude 2025-12-06T15:35:46.957889Z

({:a 1} :a) ;; get element a from map
(#{:a} :a) ;; get element from set
({:a 1} :a :not-found) ;; get element a from map, :not-found if not found
(#{:a} :a :not-found) ;; get element from set, :not-found if not found.
this isn't weird or far-fetched to me at all

👍 1
➕ 2
borkdude 2025-12-06T15:35:57.344199Z

I'm not talking about assoc, just get

2025-12-06T15:53:01.899509Z

this is worth an Ask, i think

borkdude 2025-12-06T15:54:05.203899Z

lol:

cljs.user=> (#{:a :b :c} :d :not-found)
:not-found

2025-12-06T15:54:26.045719Z

and i'd be willing to write a patch lol

borkdude 2025-12-06T15:54:59.061949Z

yeah, sure, before Ask-ing, might be good to have a response. Oh, I'm willing to write a patch too, that's the most fun part and the least amount of work.

😂 1
2025-12-06T15:57:47.451869Z

it truly is

borkdude 2025-12-06T16:21:46.032819Z

(lol, squint also supports it already, just because it treats it as a get call: https://squint-cljs.github.io/squint/?src=gzip%3AH4sIAAAAAAAAE9NQrrZKVLBKUrBKrlWwSlGwyssv0U3LL81L0QQA4r4UOxsAAAA%3D)