在clojure宏中调用特殊格式`set!`

时间:2019-01-14 07:42:36

标签: clojure macros

鉴于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))

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

1 个答案:

答案 0 :(得分:1)

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

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

您也可以(应该!)使用confobj-name来做到这一点,但是它们可能并没有积极地引起问题,至少在您提供的使用代码方面。