如何将Clojure协议扩展到另一个协议?

mik*_*era 10 abstraction protocols clojure

假设我有两个协议:

(defprotocol A 
  (f [this]))

(defprotocol B 
  (g [x y]))
Run Code Online (Sandbox Code Playgroud)

我想将协议B扩展到支持协议A的所有实例:

(extend-protocol A 
  String 
    (f [this] (.length this)))

(extend-protocol B 
  user.A
    (g [x y] (* (f x) (f y))))
Run Code Online (Sandbox Code Playgroud)

主要动机是避免必须将B分别扩展到A可能扩展到的所有可能的类,或者甚至是其他人可能扩展到A的未知未来类(例如,如果A是公共API的一部分,例如) .

但是这不起作用 - 你会得到如下内容:

(g "abc" "abcd")
=> #<IllegalArgumentException java.lang.IllegalArgumentException: 
No implementation of method: :g of protocol: #'user/B found for 
class: java.lang.String>
Run Code Online (Sandbox Code Playgroud)

这有可能吗?如果没有,是否有一个合理的解决方法来实现相同的目标?

Ale*_*art 9

协议不是类型,不支持继承.协议本质上是一个命名的函数定义集合(以及调用这些函数时的调度机制).

如果您有多个类型都碰巧具有相同的实现,您可以简单地调用一个公共函数.或者,您可以使用该地图创建方法地图和extend每种类型.例如:

(defprotocol P
  (a [p])
  (b [p]))

(deftype R [])
(deftype S [])
(deftype T [])

(def common-P-impl
  {:a (fn [p] :do-a)
   :b (fn [p] :do-b)})

(extend R
  P common-P-impl)
(extend S
  P common-P-impl)
(extend T
  P common-P-impl)

如果您提供有关实际情况的更多详细信息,我们可能会建议正确的方法.


Jon*_*nas 7

在我看来,你可以实现的功能g在以下方面f.如果是这种情况,您将拥有所需的所有多态性,而无需协议B.

我的意思是以下,因为这f是多态的,然后

(defn g [x y]
  (* (f x) (f y)))
Run Code Online (Sandbox Code Playgroud)

产生一个g支持实现协议的所有类型的函数A.

通常,当协议位于最底层时,仅根据协议功能(或者自身使用协议的其他功能)定义的简单功能使得整个命名空间/库/程序非常多态,可扩展且灵活.

序列库就是一个很好的例子.简化后,有两个多态函数,firstrest.序列库的其余部分是普通函数.