请分享您的想法,参加2024 年 Clojure 调查!

欢迎!请参阅关于页面了解更多关于如何使用本系统的情况。

0
协议

在我看来,协议的一个主要好处是协议方法是正确命名空间合格的。因此,我可以在不同的命名空间中拥有多个协议,这些协议定义了 foo 方法并扩展它们(或它们的子集),而无需使用现有的类型。然而,使用 defrecord 和 deftype 在线扩展协议,或者在 Java 端通过实现它们的接口扩展协议并不是这样。

示例

`
;; 文件:protocoltest/foo.clj
(ns prototest.foo)
(defprotocol Foo
(mymethod [this]))

;; 文件:protocoltest/bar.clj
(ns prototest.bar)
(defprotocol Bar)
(mymethod [this]))

;; 文件:protocoltest/core.clj
(ns prototest.core
(:require [prototest.foo :as foo]

        [prototest.bar :as bar]))

;;Inline 扩展两个 mymethod 方法不起作用
(defrecord MyRec [x]
foo/Foo
(mymethod [this] :foo)
bar/Bar
(mymethod [this] :bar))
;;=> java.lang.ClassFormatError
;; Duplicate method name&signature in class file prototest/core/MyRec

;; 我不得不求助于半 inline 半 dynamic...
(defrecord MyRec [x]
foo/Foo
(mymethod [this] :foo))
(extend-type MyRec
bar/Bar
(mymethod [this] :bar))

;; ... 或者完全 dynamic 扩展。
(defrecord MyRec [x])
(extend-type MyRec
foo/Foo
(mymethod [this] :foo)
bar/Bar
(mymethod [this] :bar))

;; 然后一切都会顺利。
(foo/mymethod (->MyRec 1))
;;=> :foo
(bar/mymethod (->MyRec 1))
;;=> :bar
`

我知道我得到这个错误,因为 Foo 和 Bar 接口支持协议都有 mymethod 方法,因此它们不能同时被实现(至少不能实现不同的行为)。

但为什么放弃协议使用的命名空间优势呢?例如,为什么 protocoltest.foo.Foo 方法不命名为 protocoltest$foo$mymethod(或另一个混淆名)在相应的接口中?这样,两个方法都可以在线上实现,从而获得速度优势,还可以通过 Java 端做到同样的效果。 (目前,通过 clojure.java.api 从 Java 端调用 clojure.core.extend 不可取,因为您必须构建映射,内联关键字,定义函数等。)

当然,改变默认方法命名方案的船只早已驶过,但也许可以在 defprotocol 中添加一个 :ns-qualified-method-names 选项。

1 答案

0
...