我的目标是一个像这样工作的函数/宏:
(def f (polymorphic-fn
java.lang.Long (fn [a] (inc a))
java.lang.String (fn [a] (str a "x"))))
(f 1) ;; => 2
(f "abc") ;; => "abcx"
由于基于类型的协议发送具有最佳性能,我正在考虑创建一个匿名的#39;融合的协议'用这样的宏函数:
(defmacro polymorphic-fn
[& pairs]
(let [proto (gensym)
method (gensym)
extends (for [[type handler] pairs]
`(extend ~type ~proto {(keyword ~method) ~handler}))]
`(do
(defprotocol ~proto (~method [e#]))
~@extends
~method)))
这会产生错误:Unable to resolve symbol: G__18707
。
有没有办法让“匿名”回归?方法,还是有更好的方法来实现这样的功能?
答案 0 :(得分:3)
问题是defprotocol
将生成代码,该代码将实施协议方法。 IE浏览器。在宏展开之后,编译器仍然不知道您定义的方法的符号。因此,编译会失败,并且会禁止符号未知。
许多其他def...
会生成一个宏"来电"这将在宏扩展期间实现符号(因此编译器将保持满意)。
要修复它,您可以事先声明它。这是有效的,因为declare
是宏,将得到扩展,编译器会很高兴:
(defmacro polymorphic-fn
[& pairs]
(let [proto (gensym "proto")
method (gensym "prot-method-")
extends (for [[type handler] (partition 2 pairs)]
`(extend ~type ~proto {~(keyword (str method)) ~handler}))]
`(do
(declare ~method)
(defprotocol ~proto (~method [e#]))
~@extends
~method)))
注意:我还修复了此keyword
来电。
答案 1 :(得分:1)
我认为您只想使用常规协议以及extend-type
:
(defprotocol Fooable
(foo [this]) )
(extend-type java.lang.Long
Fooable
(foo [some-long] (inc some-long)))
(extend-type java.lang.String
Fooable
(foo [any-string] (str any-string "-and-more")))
结果:
(foo 3) => 4
(foo "hello") => "hello-and-more"
可以使用宏来通过使用auto-gensym隐藏协议名称,但我没有看到这一点。只需忽略协议名称'Fooable',您就会得到相同的结果。
另外,请注意Clojure实现的一部分在幕后创建具体的Java类,这可能需要硬编码的名称。
您还可以使用cond
:
(defn bar [it]
(cond
(= (class it) java.lang.Long) (inc it)
(= (class it) java.lang.String) (str it "-and-more")))
(bar 7) => 8
(bar "farewell") => "farewell-and-more"
如果您愿意,可以像定义bar
一样定义生成polymorphic-fn
的函数。