哪个协议在 clojure 中定义了 conj?
which protocol defines conj in clojure?
假设我写了一个函数:
(defn foo [to x] (conj to x))
并希望通过说明 to
必须实施某些协议来记录它(如 structure/type to
必须支持调用 conj
)。是否有包含此信息的网站或数据库?显然我想将这个问题概括为 "where can I find a complete reference for all clojure protocols?"
作为一个使用 Sam Estep 建议的清晰具体示例,它看起来像:
(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))))
不幸的是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而不是协议。这有你预料到的缺点,但是虽然重新实现所有这些抽象作为协议适用于 ClojureScript,因为它是在引入协议之后创建的,但将它们改进到 JVM Clojure 中是不可行的。
如果您查看 conj
, you'll see that it calls clojure.lang.RT/conj
, which requires that its first argument implements the IPersistentCollection
界面的源代码。因此,您可以这样编写您的函数:
(defn foo [to x]
{:pre [(instance? clojure.lang.IPersistentCollection to)]}
(conj to x))
为了您的概括,我会向您指出 我过去问过的关于实现 Clojure 的核心接口的问题。如果上面的答案不足以解决您的问题,请告诉我,我会在此处添加更多详细信息。
我会对你的 invert-many-to-one
函数做一些小的调整:
(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))))
- 我将 arity-1 body 改为 arity-2 body 而不是 arity-3 body;这样,您只需在一处将
conj
指定为默认值。
- 我将前提条件移到了 arity-3 主体中,以便在所有情况下都会使用它。
- 我将
clojure.core/get
替换为 get
以保持惯用性。
当然,正如您在评论中指出的那样,这三个更改都有其自身的缺点,因此请务必持保留态度。
假设我写了一个函数:
(defn foo [to x] (conj to x))
并希望通过说明 to
必须实施某些协议来记录它(如 structure/type to
必须支持调用 conj
)。是否有包含此信息的网站或数据库?显然我想将这个问题概括为 "where can I find a complete reference for all clojure protocols?"
作为一个使用 Sam Estep 建议的清晰具体示例,它看起来像:
(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))))
不幸的是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而不是协议。这有你预料到的缺点,但是虽然重新实现所有这些抽象作为协议适用于 ClojureScript,因为它是在引入协议之后创建的,但将它们改进到 JVM Clojure 中是不可行的。
如果您查看 conj
, you'll see that it calls clojure.lang.RT/conj
, which requires that its first argument implements the IPersistentCollection
界面的源代码。因此,您可以这样编写您的函数:
(defn foo [to x]
{:pre [(instance? clojure.lang.IPersistentCollection to)]}
(conj to x))
为了您的概括,我会向您指出
我会对你的 invert-many-to-one
函数做一些小的调整:
(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))))
- 我将 arity-1 body 改为 arity-2 body 而不是 arity-3 body;这样,您只需在一处将
conj
指定为默认值。 - 我将前提条件移到了 arity-3 主体中,以便在所有情况下都会使用它。
- 我将
clojure.core/get
替换为get
以保持惯用性。
当然,正如您在评论中指出的那样,这三个更改都有其自身的缺点,因此请务必持保留态度。