Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

which protocol defines conj in clojure?

lets say I wrote a function:

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

and would like to document it by stating that to must implement some protocol (as in the structure/type to must support the call conj). Is there a website or database that has this information? Obviously I would like to generalise this question to "where can I find a complete reference for all clojure protocols?"

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

(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 [k v]]
              (assoc! m v (insert-fn (clojure.core/get m v (constructor-fn)) k)))
            (transient {}) m))))
like image 404
beoliver Avatar asked Jul 21 '17 22:07

beoliver


People also ask

What is conj Clojure?

Clojure/conj is the original conference for Clojure and its community. Founded in 2010, the conference is the premier place for developers from all around the world to gather and learn about what is happening with the language, in the community, and within organizations using Clojure.

What is nil Clojure?

nil. nil is a possible value of any data type in Clojure. nil has the same value as Java null. The Clojure conditional system is based around nil and false, with nil and false representing the values of logical falsity in conditional tests - anything else is logical truth.


1 Answers

Unfortunately protocols weren't introduced until Clojure 1.2, and by then, all the built-in data structure abstractions had already been implemented as Java interfaces instead of protocols. This has the disadvantages you would expect, but while reimplementing all those abstractions as protocols was appropriate for ClojureScript since it was created after protocols were introduced, it would be infeasible to retrofit them into the JVM Clojure.

If you look at the source code for conj, you'll see that it calls clojure.lang.RT/conj, which requires that its first argument implements the IPersistentCollection interface. Thus, you could write your function like this:

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

For your generalization, I would point you to a question that I asked in the past about implementing Clojure's core interfaces. If the answers there are not sufficient for your question, please let me know and I will add more details here.

I would make a few minor adjustments 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 [k v]]
              (assoc! m v (insert-fn (get m v (constructor-fn)) k)))
            (transient {}) m))))
  • I changed the arity-1 body to call the arity-2 body instead of the arity-3 body; this way, you only specify conj as the default in one place.
  • I moved the precondition into the arity-3 body so that it will be used in all cases.
  • I replaced clojure.core/get with just get for idiomaticity.

Of course, these three changes have their own drawbacks, as you pointed out in your comments, so definitely take them with a grain of salt.

like image 63
Sam Estep Avatar answered Oct 09 '22 23:10

Sam Estep