在 clojure 宏中调用特殊形式“set!”



给定Java实例obj和成员名称(字符串)"Foo",以及映射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))

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

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

无论如何,该特定问题的解决方法(无论它是否是您描述的问题的根本原因)是将表单包装在(let [obj# ~obj] ...)中,然后参考下面的obj#,因此参数仅插值一次。

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

最新更新