Just to provide a counterpoise to Arthur's answer: conj
is defined even earlier (the name conj
appears on line 82 of core.clj vs.1443 for disj
and 1429 for dissoc
) and yet works on all Clojure collection types. :-) Clearly it doesn't use protocols – instead it uses a regular Java interface, as do most Clojure functions (in fact I believe that currently the only piece of "core" functionality in Clojure that uses protocols is reduce
/ reduce-kv
).
I'd conjecture that it's due to an aesthetic choice, and indeed probably related to the way in which maps support conj
– were they to support disj
, one might expect it to take the same arguments that could be passed to conj
, which would be problematic:
;; hypothetical disj on map
(disj {:foo 1
[:foo 1] 2
{:foo 1 [:foo 1] 2} 3}
}
{:foo 1 [:foo 1] 2} ;; [:foo 1] similarly problematic
)
Should that return {}
, {:foo 1 [:foo 1] 2}
or {{:foo 1 [:foo 1] 2} 3}
? conj
happily accepts [:foo 1]
or {:foo 1 [:foo 1] 2}
as things to conj
on to a map. (conj
with two map arguments means merge
; indeed merge
is implemented in terms of conj
, adding special handling of nil
).
So perhaps it makes sense to have dissoc
for maps so that it's clear that it removes a key and not "something that could be conj
'd".
Now, theoretically dissoc
could be made to work on sets, but then perhaps one might expect them to also support assoc
, which arguably wouldn't really make sense. It might be worth pointing out that vectors do support assoc
and not dissoc
, so these don't always go together; there's certainly some aesthetic tension here.