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]))

;; 内联扩展两个mymethod方法不起作用
(defrecord MyRec [x]
foo/Foo
(mymethod [this] :foo)
bar/Bar
(mymethod [this] :bar))
;;=> java.lang.ClassFormatError
;; 类文件prototest/core/MyRec中重复了方法名称和签名

;; 我不得不求助于部分内联和部分动态...
(defrecord MyRec [x]
foo/Foo
(mymethod [this] :foo))
(extend-type MyRec
bar/Bar
(mymethod [this] :bar))

;; ... 或者完全动态扩展。
(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
by
...