Why are the `disj` and` dissoc` functions different from Clojure?

As far as I saw, the main functions of Clojure almost always work for different types of collections, for example. conj , first , rest , etc. I am a little puzzled why disj and dissoc are different from each other; they have the same signature:

 (dissoc map) (dissoc map key) (dissoc map key & ks) (disj set) (disj set key) (disj set key & ks) 

and pretty similar semantics. Why are they not covered by the same function? The only argument I can see in favor of this is that maps have both (assoc map key val) and (conj map [key val]) to add entries, and sets only support (conj set k) .

I can write a one-line function to handle this situation, but Clojure is so elegant that it really annoys me when it's not :)

+6
source share
3 answers

Just to give an answer to Arthur: conj is defined even earlier (the conj name appears on line 82 of the .clj vs .1443 kernel for disj and 1429 for dissoc ) and still works on all types of the Clojure collection. :-) Obviously, it does not use protocols - instead, it uses the usual Java interface, like most Clojure functions (in fact, I believe that at present the only part of the β€œcore” functionality in Clojure that uses protocols is reduce / reduce-kv ).

I would suggest that this is because of the aesthetic choice and is probably related to how the cards support conj , whether they should support disj , we can expect him to accept the same arguments that could be switched 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 they 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 conj on the map. ( conj with two map arguments means merge , indeed merge is implemented in conj terms, adding special nil processing).

Therefore, it may make sense to have a dissoc for the cards so that it clears that it removes the key, rather than "what could be conj " d ".

Now, theoretically, dissoc could work on sets, but then perhaps one would expect them to support assoc , which might not make sense. It might be worth noting that vectors support assoc , not dissoc , so they don't always go together; there, of course, there is some kind of aesthetic tension.

+5
source

I always doubt trying to answer for the motives of others, although I strongly suspect that this is a bootstrap problem in core.clj. both of these functions are defined quite early in core.clj and are almost identical, except that each of them takes exactly one type and calls a method on it directly.

 (. clojure.lang.RT (dissoc map key)) 

and

 (. set (disjoin key)) 

both of these functions are defined before the protocol is defined in core.clj , so they cannot use the protocol to send between them based on the type. Both of these parameters are also defined in the language specification before protocols exist. They are also called often enough that there will be a strong incentive to make them as fast as possible.

+4
source
  (defn del "Removes elements from coll which can be set, vector, list, map or string" [ coll & rest ] (let [ [ w & tail ] rest ] (if w (apply del (cond (set? coll) (disj coll w) (list? coll) (remove #(= w %) coll) (vector? coll) (into [] (remove #(= w % ) coll)) (map? coll) (dissoc coll w) (string? coll) (.replaceAll coll (str w) "")) tail) coll))) 

Who needs this? Just use the function above and forget about the past ...

+1
source

Source: https://habr.com/ru/post/983293/


All Articles