Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何将Clojure协议扩展到另一个协议?_Clojure_Protocols_Abstraction - Fatal编程技术网

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

如何将Clojure协议扩展到另一个协议?,clojure,protocols,abstraction,Clojure,Protocols,Abstraction,假设我有两个协议: (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可能扩展到的所有可能类,甚至是其他人可

假设我有两个协议:

(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>
(g“abc”“abcd”)

=>#协议不是类型,不支持继承。协议本质上是函数定义的命名集合(以及调用这些函数时的分派机制)

如果有多个类型恰好具有相同的实现,则可以简单地调用一个公共函数。或者,您可以创建一个方法映射,并使用该映射扩展每个类型。例如:

(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) (DEFP协议) (a[p]) (b[p])) (deftype R[])) (deftype S[]) (灵活类型T[]) (def通用-P-impl {:a(fn[p]:do-a) :b(fn[p]:do-b}) (扩展R) P公共-P-impl) (续) P公共-P-impl) (扩展T P公共-P-impl)
如果您提供有关实际场景的更多详细信息,我们可能会建议正确的方法。

在我看来,您可以按照
f
实现功能
g
。如果是这种情况,那么您就拥有了不使用协议
B
所需的所有多态性

我的意思是,假设
f
是多态的,那么

(defn g [x y]
  (* (f x) (f y)))
生成一个函数
g
,该函数支持实现协议
a
的所有类型

通常,当协议处于最底层时,仅根据协议函数(或在自己使用协议的其他函数上)定义的简单函数会使整个名称空间/库/程序具有多态性、可扩展性和灵活性


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

虽然我不完全理解您要做的事情,但我想知道clojure multimethods是否是解决您问题的更好的解决方案。

据我所见,协议确实可以扩展协议。 我在这里举了一个例子:

在本例中,我们有一个
Animalia
协议(允许其成员做
dream
),该协议由
Erinaceinae
协议扩展(允许其成员快速

我们有一个记录
Hedgehog
,它是
Erinaceinae
协议的一部分。我们的记录实例可以是
梦想
快速

(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.
在“Clojure应用”中,有一个按协议扩展协议的方法

(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)))))
书上说这是不可能的。我和亚历克斯·米勒谈过这件事,他说:

(g "abc" "abcd")
=> #<IllegalArgumentException java.lang.IllegalArgumentException: 
No implementation of method: :g of protocol: #'user/B found for 
class: java.lang.String>
这并不是在所有情况下都有效。该协议生成一个Java接口,您可以肯定地将协议扩展到该接口。 问题在于,并非每个协议实现都实现了该接口-仅使用内联声明(如
(defrect Foo[a]TheProtocol(Foo…)
)实现该接口的记录或类型。如果您使用
扩展类型
扩展协议
实现协议,则协议接口的扩展不会捕获这些实例。所以,你真的不应该这么做:)


谢谢我认为这是在我的例子中最好的方法——与序列库的类比在这里很有效!这也是我的解决方案-我认为可以利用
map
来消除重复,例如
(map#(extend%P common-P-impl)[R S T])
对不起,
doseq
,或者应该使用封闭的
doall
,因为map是懒惰的。。