假设我有两个协议:
(defprotocol A
(f [this]))
(defprotocol B
(g [x y]))
我想将协议B扩展到支持协议A的所有实例:
(extend-protocol A
String
(f [this] (.length this)))
(extend-protocol B
user.A
(g [x y] (* (f x) (f y))))
主要的动机是避免必须将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>
这有可能吗?如果没有,是否有一个合理的解决方法来实现相同的目标?
答案 0 :(得分:9)
协议不是类型,不支持继承。协议本质上是一个命名的函数定义集合(以及调用这些函数时的调度机制)。
如果您有多个类型都碰巧具有相同的实现,您可以简单地调用一个公共函数。或者,您可以使用该地图创建方法地图和extend
每种类型。 E.g:
(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)
如果您提供有关实际情况的更多详细信息,我们可能会建议正确的方法。
答案 1 :(得分:7)
在我看来,你可以用g
来实现f
函数。如果是这种情况,您将拥有所需的所有多态性,而无需协议B
。
我的意思是以下,假设f
是多态的,那么
(defn g [x y]
(* (f x) (f y)))
产生一个函数g
,它支持所有实现协议A
的类型。
通常,当协议位于最底层时,仅根据协议功能(或自身使用协议的其他功能)定义的简单功能使整个命名空间/库/程序非常多态,可扩展且灵活。
序列库就是一个很好的例子。简化后,有两个多态函数first
和rest
。序列库的其余部分是普通函数。
答案 2 :(得分:2)
在“Clojure applied”中有一个按协议扩展协议的方法
(extend-protocol TaxedCost
Object
(taxed-cost [entity store]
(if (satisfies? Cost entity)
(do (extend-protocol TaxedCost
(class entity)
(taxed-cost [entity store]
(* (cost entity store) (+ 1 (tax-rate store)))))
(taxed-cost entity store))
(assert false (str "Unhandled entity: " entity)))))
实际上没有什么可以阻止你简单地将协议扩展到另一个
(extend-protocol TaxedCost
Cost
(taxed-cost [entity store]
(* (cost entity store) (+ 1 (tax-rate store)))))
虽然书上说这是不可能的。我和亚历克斯·米勒谈过这件事,他说了以下几点:
它确实不适用于所有情况。该协议生成Java接口,您可以确定将协议扩展到该接口。 问题是并非每个协议实现都实现了该接口 - 只有使用内联声明(如
(defrecord Foo [a] TheProtocol (foo ...))
)的记录或类型。如果您使用extend-type
或extend-protocol
实现协议,那么这些实例将不会被协议接口的扩展捕获。所以,你真的不应该这样做:)
答案 3 :(得分:1)
从我看来,协议确实可以扩展协议。 我在这里做了一个例子:https://github.com/marctrem/protocol-extend-protocol-example/blob/master/src/extproto/core.clj
在示例中,我们有Animalia
协议(允许其成员执行dream
),该协议由Erinaceinae
协议扩展(允许其成员go-fast
)。
我们有一个记录Hedgehog
,它是Erinaceinae
协议的一部分。我们的记录实例可以dream
和go-fast
。
(ns extproto.core
(:gen-class))
(defprotocol Animalia (dream [this]))
(defprotocol Erinaceinae (go-fast [this]))
(extend-protocol Animalia
extproto.core.Erinaceinae
(dream [this] "I dream about things."))
(defrecord Hedgehog [lovely-name]
Erinaceinae
(go-fast [this] (format "%s the Hedgehog has got to go fast." (get this :lovely-name))))
(defn -main
[& args]
(let [my-hedgehog (Hedgehog. "Sanic")]
(println (go-fast my-hedgehog))
(println (dream my-hedgehog))))
;1> Sanic the Hedgehog has got to go fast.
;1> I dream about things.
答案 4 :(得分:0)
虽然我并不完全明白你想要做什么,但我想知道clojure multimethods是否能更好地解决你的问题。