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))))
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.
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.
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))))
conj
as the default in one place.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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With