Reputation: 113
Given Java instance obj
and a member name (string) "Foo"
, and a map conf
, I am trying to generate Clojure code that will look like this:
(if (get conf "Foo")
(set! (.Foo obj) (get conf "foo")
obj)
And also, if I know that "SomeEnum"
is a Java enum name, a code like this:
(if (get conf "SomeEnum")
(set! (.someEnum obj)(Enum/valueOf SomeEnum (get conf "SomeEnum")))
obj)
Here is what I came up with:
(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))
Testing with macroexpand
seems to give the right result, but after trying:
(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")))
I got a weird endless loop of reflection warnings.
--- edit ---
after battelling with that for quite a while, I gave up threading (->
) and ended with:
(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))
So I am still not sure what was the reflection warning endless loop about, but this is hopefully handful as well, and can be improved further.
Upvotes: 0
Views: 91
Reputation: 1332
I think this is due to your interpolating ~obj
multiple times in each macro. Is your "loop" actually endless, or is it just ~128 or ~256 steps long?
In any case, the fix for that particular issue (whether or not it's the root cause of the problem you describe) is to wrap the form in (let [obj# ~obj] ...)
and then refer to obj#
below, so the argument is only interpolated once.
You can (should!) do this with conf
and obj-name
too, but they're probably not actively causing problems, at least with the usage code you provided.
Upvotes: 1