首页 > 解决方案 > 在 clojure 宏中调用特殊形式 `set!`

问题描述

给定 Java 实例obj和成员名称(字符串)"Foo"和 map conf,我正在尝试生成如下所示的 Clojure 代码:

(if (get conf "Foo")
    (set! (.Foo obj) (get conf "foo")
    obj)

而且,如果我知道这"SomeEnum"是一个 Java 枚举名称,那么代码如下:

(if (get conf "SomeEnum")
    (set! (.someEnum obj)(Enum/valueOf SomeEnum (get conf "SomeEnum")))
    obj)

这是我想出的:

(defmacro set-java [obj conf obj-name]
  `(if (get ~conf ~obj-name)
     (set! (. ~obj ~(symbol obj-name)) (get ~conf ~obj-name))
     ~obj))

(defn lowercase-first [s]
  (apply str (Character/toLowerCase (first s)) (rest s)))

(defmacro set-java-enum [obj conf obj-name]
  `(if (get ~conf ~obj-name)
     (set! (. ~obj ~(symbol (lowercase-first obj-name)))
           (Enum/valueOf ~(symbol obj-name) (get ~conf ~obj-name)))
     ~obj))

测试macroexpand似乎给出了正确的结果,但在尝试之后:

(defn ^Policy map->policy [conf]
  (-> (Policy.)
      (set-java-enum conf "CommitLevel")
      (set-java conf "durableDelete")
      (set-java conf "expiration")
      (set-java conf "generation")
      (set-java-enum conf "GenerationPolicy")
      (set-java-enum conf "RecordExistsAction")
      (set-java conf "respondAllOps")))

我得到了一个奇怪的无休止的反射警告循环。

- - 编辑 - -

在与它斗争了很长一段时间后,我放弃了线程(->)并以:

(defmacro set-java [obj conf obj-name]
  `(when (get ~conf ~obj-name)
     (set! (. ~obj ~(symbol obj-name)) (get ~conf ~obj-name))))

(defn lowercase-first [s]
  (apply str (Character/toLowerCase ^Character (first s)) (rest s)))

(defmacro set-java-enum [obj conf obj-name]
  `(when (get ~conf ~obj-name)
     (set! (. ~obj ~(symbol (lowercase-first obj-name)))
           (Enum/valueOf ~(symbol obj-name) (get ~conf ~obj-name)))))

(defn map->write-policy [conf]
  (let [wp (WritePolicy. (map->policy conf))]
    (set-java-enum wp conf "CommitLevel")
    (set-java wp conf "durableDelete")
    ;; more non threaded object manipulation
    wp))

所以我仍然不确定反射警告无限循环是什么,但希望这也是少数,并且可以进一步改进。

标签: clojuremacros

解决方案


我认为这是由于您~obj在每个宏中多次插值。你的“循环”实际上是无穷无尽的,还是只有 ~128 或 ~256 步长?

在任何情况下,针对该特定问题的修复(无论它是否是您描述的问题的根本原因)是将表单包装起来(let [obj# ~obj] ...),然后参考obj#下面,因此该参数仅插入一次。

您也可以(应该!)这样做confobj-name但它们可能不会主动引起问题,至少使用您提供的使用代码。


推荐阅读