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.
source share