首页 > 解决方案 > 通过“匿名”协议和方法将类型/fn 对融合为多态 fn

问题描述

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

(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"

由于基于类型的协议分派具有最佳性能,我正在考虑使用如下宏为“融合”函数创建一个“匿名”协议:

(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

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

标签: clojure

解决方案


问题是defprotocol它将生成将实习协议方法的代码。IE。在宏扩展之后,编译器仍然不知道您定义的方法的符号。因此,编译失败并且会抱怨符号是未知的。

许多其他def...'s 将生成一个宏“调用”,该宏“调用”将在宏扩展期间实习符号(因此编译器将保持满意)。

要修复它,您可以事先声明它。这是可行的,因为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调用。


推荐阅读