Clojure 多态(Polymorphism)
Table of Contents
defmulti & defmethod
Java 中,多态通过继承链达成,可以简单理解成,调用对象的方法时,使用对象的类型,沿着继承链 dispatch 到某个类上。
在 Clojure 中,Multimethods 对 dispatch 这种模式进行抽象,defmulti 中定义的 dispatch 函数对于给定一组参数,生成一个用于 dispatch 的值,这个值被用于匹配定位特定一个 defmethod 实现,匹配使用 isa?.
由于 Class 的继承关系满足 isa? 关系,因此上面 Java
中的继承链多态可以简单模拟成:
另外 Clojure 中可以用
derive
函数构建 symbol 或者 keyword 之间的
isa? 关系,你可以1:
(defmulti area :Shape)(defmethod area :Rect [r] (* (:wd r) (:ht r)))(defmethod area :Circle [c] (* (. Math PI) (* (:radius c) (:radius c))))(defmethod area :default [x] :oops)(area {:Shape :Rect :wd 4 :ht 3}) ; => 12(area {:Shape :Circle :radius 3}) ; => 28.274333882308138(area {:Shape :SomeUnkownShape}) ; => :oops
protocols
对于 protocol 的说明,这里拷贝翻译一些官方文档2的内容。
protocol 由一组方法签名构成,可以通过
defprotocol 进行定义:
(defprotocol AProtocol"A doc string for AProtocol abstraction"(bar [a b] "bar docs")(baz [a] [a b] [a b c] "baz docs"))
- 不用给定实现
- 可以可选的给定一个文档字符串
- 上面的定义会生成一组多态的函数(polymorphic function)和一个 protocol 对象
- 生成的这组函数将通过它们的第一个参数进行 dispatch,即所有方法至少有一个参数
我们可以在 defrecord, deftype 时直接实现 protocol,也能使用 reify 直接创建 protocol 的一个实例。
(defprotocol P (echo [x]))(defrecord Foo [a]P (echo [x] a))(deftype Bar [a]P (echo [x] a))(echo (Foo. 42)) ; => 42(echo (Bar. 42)) ; => 42(echo (reify P (echo [x] 42))) ; => 42
也可以直接扩展某个已经定义(可能这个定义不受你控制)的类型实现特定 protocol.
(defprotocol P (bar [x]))(defprotocol Q (baz [x]))(defrecord Foo [a]);; 扩展一个类型,为其实现多个 protocol(extend FooP {:bar (fn [x] [:bar (:a x)])}Q {:baz (fn [x] [:baz (:a x)])})(bar (Foo. 42)) ; => [:bar 42](baz (Foo. 42)) ; => [:baz 42]
有两个展开到 extend 的 macro 方便 extend 的使用:
;; 这个简化了 extend 的书写(extend-type FooP (bar [x] [:bar (:a x)])Q (baz [x] [:baz (:a x)]));; 简化多个类型实现一个 protocol 的书写(extend-protocol PAType (bar [x] [:a-type (:a x)])BType (bar [x] [:b-type (:a x)]))
Clojure 1.10 开始,支持通过 metadata 实现 protocol:
(defprotocol S:extend-via-metadata true(echo [s]))(def a-dict {:name "42"})(def a-dict-with-meta (with-meta a-dict {`echo (fn [s] (:name s))}))(echo a-dict-with-meta) ; => 42
Footnotes:
这段示例来自官方文档:https://clojure.org/reference/multimethods, 这里有对 Multimethods 比较详尽的解释.