What protocol defines conj in clojure?

will say that I wrote a function:

(defn foo [to x] (conj to x)) 

and I would like to document it, stating that to should implement some protocol (since the conj call must be supported in the structure / type of to ). Is there a website or database that has this information? Obviously, I would like to generalize this question to "where can I find the full link for all clojure protocols?"

As a clear and concrete example, using Sam Estep's suggestion, it would look like this:

 (defn invert-many-to-one "returns a one-to-many mapping where vals are collections of type `(constructor-fn)`, (defaults to `hash-set`). Note that `constructor-fn` is a function of 0 args. `insert-fn` function can be passed. if only `constructor-fn` is passed then `insert-fn` defaults to `conj` and `(constructor-fn)` must be an instance of `clojure.lang.IPersistentCollection`" ([m] (invert-many-to-one hash-set conj m)) ([constructor-fn m] {:pre [(instance? clojure.lang.IPersistentCollection (constructor-fn))]} (invert-many-to-one constructor-fn conj m)) ([constructor-fn insert-fn m] (persistent! (reduce (fn [m [kv]] (assoc! mv (insert-fn (clojure.core/get mv (constructor-fn)) k))) (transient {}) m)))) 
+5
source share
1 answer

Unfortunately, protocols were not introduced before Clojure 1.2, and by then all the built-in abstractions of the data structure were already implemented as Java interfaces instead of protocols. This has drawbacks that you would expect, but if all these abstractions are repeated, since the protocols were suitable for ClojureScript, since it was created after the protocols were introduced, it would be unreasonable to modify them in the Clojure JVM.

If you look at the source code for conj , you will see that it calls clojure.lang.RT/conj , which requires that its first argument implement the IPersistentCollection interface. Thus, you can write your function as follows:

 (defn foo [to x] {:pre [(instance? clojure.lang.IPersistentCollection to)]} (conj to x)) 

For your generalization, I would like to point you to a question that I asked in the past about the implementation of the main Clojure interfaces. If the answers to your question are not enough, let me know and I will add more details here.

I would make a few minor changes to your invert-many-to-one function:

 (defn invert-many-to-one "Returns a one-to-many mapping where vals are collections of type `(constructor-fn)` (defaults to `hash-set`). Note that `constructor-fn` is a function of 0 args. `insert-fn` function can be passed. If only `constructor-fn` is passed then `insert-fn` defaults to `conj`. `(constructor-fn)` must be an instance of `clojure.lang.IPersistentCollection`." ([m] (invert-many-to-one hash-set m)) ([constructor-fn m] (invert-many-to-one constructor-fn conj m)) ([constructor-fn insert-fn m] {:pre [(instance? clojure.lang.IPersistentCollection (constructor-fn))]} (persistent! (reduce (fn [m [kv]] (assoc! mv (insert-fn (get mv (constructor-fn)) k))) (transient {}) m)))) 
  • I changed the body of arity-1 to call the body of arity-2 instead of the body of arity-3; this way you only specify conj as the default in one place.
  • I moved the precondition to the arity-3 body so that it is used in all cases.
  • I replaced clojure.core/get with get only for idiomaticity.

Of course, these three changes have their drawbacks, as you indicated in your comments, so definitely take them with salt.

+6
source

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


All Articles