interface - 使用协议(protocol) 'static' 在 Clojure 中描述调度是否准确?

标签 interface clojure static protocols dispatch

Meikel Brandmeyerdispatch in Clojure上写了一篇文章URL 标题为静态与动态。他写道:

Protocols are not the only place where we have a trade-off of static vs. dynamic. There are several places where such a trade-off can be spotted.

他提供了协议(protocol)中的以下静态调度示例:

(defprotocol Flipable
  (flip [thing]))

(defrecord Left  [x])
(defrecord Right [x])

(extend-protocol Flipable
  Left
  (flip [this] (Right. (:x this)))
  Right
  (flip [this] (Left. (:x this))))

现在,每条记录都映射到 JVM 上已编译的“类”,这是事实。如果您尝试分派(dispatch) LeftRight 以外的任何内容,您将收到 java.lang.IllegalArgumentExceptionNo implementation方法:...找到类:...

我问这个问题是因为我的理解是 Clojure 在幕后有效地使用相同的 JVM 技术进行多态调度。我们可以将上面的内容重写为:

interface Flippable {
  Flippable flip();
}

class Left implements Flippable {
  Right flip();
}

class Right implements Flippable {
  Left flip();
}

class Demo {
  public static void main(String args[]) {
    Flippable flippable = new Right();
    System.out.println(flippable.flip);
  }
}

现在,虽然类型已编译并静态检查,但实际调度是在运行时进行的。

我的问题是:将 Clojure 中使用协议(protocol)的调度描述为“静态”是否准确?(假设您没有使用 map 进行调度,而是依赖于以下记录或类型)对应一个类)。

最佳答案

Clojure 的协议(protocol)实现是单分派(dispatch)类型驱动的多态性(对函数第一个参数的类型进行多态性),因此是动态多态性的一种形式。

使用extend-protocol不会导致静态绑定(bind)。 extend-protocol 是一个宏,仅扩展为 extend 调用:

(clojure.pprint/pprint
 (clojure.walk/macroexpand-all '(extend-protocol Flipable
                                  Left
                                  (flip [this] (Right. (:x this)))
                                  Right
                                  (flip [this] (Left. (:x this))))))

;=>     
(do
 (clojure.core/extend
  Right
  Flipable
  {:flip (fn* ([this] (new Left (:x this))))})
 (clojure.core/extend
  Left
  Flipable
  {:flip (fn* ([this] (new Right (:x this))))}))

您是正确的,要调用的函数是在运行时使用底层 JVM 的动态调度机制动态确定的。这为协议(protocol)提供了优于多方法的性能优势,同时将调度限制为第一个参数的类型。

deftype(或 reify)定义中内联扩展协议(protocol)与将协议(protocol)扩展到现有类型(使用扩展*变体)会导致性能差异。内联 deftype 与其实现的协议(protocol)方法一起编译成 Java 类,因此直接实现协议(protocol)方法。

协议(protocol)方法调用检查第一个参数是否直接实现协议(protocol),以及是否直接在对象上调用方法,而不是查找适当的方法实现。

还有详细的基准分析可用 here 。 Clojure源码中的相关函数可用here

关于interface - 使用协议(protocol) 'static' 在 Clojure 中描述调度是否准确?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28130991/

相关文章:

java - 接口(interface)永远不能在java中实例化,但是当接口(interface)使用返回类型时发生了什么

c++ - 删除 constexpr 会改变链接吗?

interface - 有没有更简单的方法在 F# 中隐式使用接口(interface)?

Java:接口(interface)与抽象类(关于字段)

clojure - 在 Liberator 中返回 201 Created 响应中的 Location header

Clojure:减法中的不一致舍入

html - Node 中的简单静态 HTML 服务器

java - 为什么 Java 不允许重写静态方法?

java - 在接口(interface)中使用 Java 泛型来强制实现以实现类型作为参数的方法

clojure - 有没有人知道 DSL 设计的好引用?