熔丝类型/ fn通过'匿名'成对多态fn协议&方法

时间:2018-04-24 19:59:26

标签: clojure

我的目标是一个像这样工作的函数/宏:

(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

有没有办法让“匿名”回归?方法,还是有更好的方法来实现这样的功能?

2 个答案:

答案 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的函数。