如何写 defprotocol
(和 defrecord
来实现它)声明一个与现有函数同名的方法,并动态分派(dispatch)给协议(protocol)/记录的方法,前提是我用协议(protocol)/记录的实例调用它,否则分派(dispatch)给现有的功能?
例如,我想创建一个支持基本算术的几何助手(在这个例子中只是乘法,以保持简短):
(defprotocol SizeOps
(* [this factor] "Multiply each dimension by factor and return a new Size"))
在这一点上,我已经从编译器中得到了一些不祥的回击:
Warning: protocol #'user/SizeOps is overwriting function *
WARNING: * already refers to: #'clojure.core/* in namespace: user, being replaced by: #'user/*
然后实现:
(defrecord Size [width height]
SizeOps
(* [this factor] (Size. (* width factor) (* height factor))))
编译没问题,但是当我尝试使用它时,唯一的
*
它知道我的协议(protocol)中的那个:(* (Size. 1 2) 10)
IllegalArgumentException No implementation of method: :* of protocol: #'user/SizeOps found for class: java.lang.Long
我可以通过完全指定核心
*
来解决这个问题我的实现中的功能:(defrecord Size [width height]
SizeOps
(* [this factor] (Size. (clojure.core/* width factor) (clojure.core/* height factor))))
(* (Size. 1 2) 10)
#user.Size{:width 10, :height 20}
但我得到相同的
IllegalArgumentException
如果我尝试调用 (* 3 4)
稍后的。我可以使用命名空间 clojure.core/*
在我的defrecord
实现,但我希望我的用户能够调用 *
在我的 Size
记录以及 Long
, Double
等,像往常一样。类似问答:
String
与 *
像 Python 一样工作的运算符:(* "!" 3)
⇒ "!!!"
, 但掩盖了核心的 *
就像我的例子 (ns user (:refer-clojure :exclude [*]))
等核心功能避免了“覆盖”警告,但也避免了该功能:( 我怀疑正确的解决方案在于较低级别的调度功能,如
defmulti
和 defmethod
或 deftype
/derive
但我对 Clojure 的 runtime polymorphism 的细微差别不太熟悉.而且我会有一大堆Size
, Point
, Rectangle
, Circle
等,每个类型都支持+
的某个子集, -
, *
, /
操作,所以我很想知道是否有办法告诉 defprotocol
参与/构建任何现有函数的多态性,而不是简单地覆盖它们。
最佳答案
在这种情况下,当您遇到协议(protocol)本身的限制时,它可以帮助创建一个单独的函数,该函数简单地调用协议(protocol)方法以实现其某些功能,并使用附加的完成需要完成的其余部分赋予常规 defn
的功能年代:
(ns example.size
(:refer-clojure :exclude [*])
(:require [clojure.core :as clj]))
(defprotocol SizeOps
(times [this factor]))
(extend-protocol SizeOps
Object
(times [this factor] (clj/* this factor)))
(defrecord Size [width height]
SizeOps
(times [this factor] (->Size (clj/* width factor) (clj/* height factor))))
(defn *
([] (clj/*))
([x] (clj/* x))
([x y] (times x y))
([x y & more] (apply clj/* x y more)))
我在这里采用的具体方法有几个优点:
clojure.core/*
相同常规旧号码 随意根据需要优化其中的任何内容。
最后,为了证明:
(ns example.core
(:refer-clojure :exclude [*])
(:require [example.size :refer [* ->Size]]))
(* (->Size 1 2) 10) ;=> #example.size.Size{:width 10, :height 20}
(* 3 4) ;=> 12
如前所述,希望足够符合人体工程学。
关于clojure - 使 Clojure 的 defprotocol 与现有函数很好地(多态地)配合使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42196881/