2

我在交叉命名空间中有一个协议:

(ns xxx.shared.interfaces)

(defprotocol ITimer
  (seconds [timer] "Return time in seconds since timer began"))

我有一个 Clojure 的实现:

(ns xxx.time
  (:require [xxx.shared.interfaces :refer [ITimer]]))

(defrecord Timer [start-nanos]
  ITimer
  (seconds [timer] (double (/ (- (System/nanoTime) (:start-nanos timer))
                              1000000000))))

问题是,当我在某些 Clojure 代码中使用此代码时,需要xxx.time使用它的命名空间:refer :all会抱怨它找不到seconds

Unable to resolve symbol: seconds in this context

首先,是否可以通过这种方式共享协议?

其次,任何想法我怎样才能使这项工作?

第三,这真的是进行这种代码共享的好方法吗?理想情况下,我也想分享记录,但它依赖于 Java 代码,所以我需要将其分解为一个函数。这会是解决这个问题的更好方法吗?

谢谢!

4

1 回答 1

6

seconds is defined in xxx.shared.interfaces, so that's what you need to :require / :use to be able to call it:

(ns foo
  (:require [xxx.shared.interfaces :refer [seconds]]))

;; (seconds ...) will now work

To elaborate, a defprotocol expression creates a Var holding the protocol, an underlying JVM interfaces on the JVM side (no counterpart to this on the ClojureScript side) and a Var for each of the protocol methods. The protocol methods can then be called through those Vars just like any other Clojure function.

When you implement a protocol for a record / type, as you do in your xxx.time namespace, you don't even need to pull in those Vars if you don't actually call the methods (a separate issue from providing implementations). Similarly, a namespace which needs to call the protocol methods, but doesn't much care about the particular types of objects it calls them on, would only need to pull in the protocol-defining namespace and use the relevant Vars and would not need to :require any implementing namespaces.

于 2013-08-30T20:23:52.543 回答