Trying to optimize a small rendering helper by avoiding map destructuring at runtime using :inline. My goal is to pass a map at call site but have it expanded into direct field access.
Is this a correct/idiomatic use of :inline?
(defn rect
{:inline (fn [m]
`(let [m# ~m
~(with-meta 'r {:tag `ShapeRenderer}) (:renderer m#)
c# (:color m#)]
(when c# (set-color ~'r c#))
(.rect ~'r (float (:x m#)) (float (:y m#)) (float (:width m#)) (float (:height m#)))))
:inline-arities #{1}}
[{:keys [^ShapeRenderer renderer x y width height color]}]
(when color (set-color renderer color))
(.rect renderer (float x) (float y) (float width) (float height)))
(rect {:renderer renderer
:x 10
:y 20
:width 100
:height 50
:color some-color})There's nothing in this code that would turn run-time value lookup into a "direct field access". I'm not even sure what the latter means when it comes to maps.
Do you mean that when something like {:x 10} is used and the run-time value would be retrieved via (:x m), the inline version would just have 10 there, inlined?
If so, note that it would behave exactly like a macro - the map that you pass in would have to be a literal map. Not a symbol that's bound to a map, not a form that produces a map, but a {...} literal.
Otherwise it wouldn't work at all, despite there being a non-inline definition.
If you're fine with that, this is something that it would look like (haven't tested):
(defn rect
{:inline (fn [m]
(assert (map? m))
(let [{:keys [renderer x y width height color]} m]
`(let [~(with-meta 'renderer {:tag `ShapeRenderer}) ~renderer
~'color ~color]
(when ~'color (set-color ~'color ~'renderer))
(.rect ~'renderer (float ~x) (float ~y) (float ~width) (float ~height)))))
:inline-arities #{1}}
[{:keys [^ShapeRenderer renderer x y width height color]}]
(when color (set-color renderer color))
(.rect renderer (float x) (float y) (float width) (float height)))But also note that map destructuring is extremely cheap. Chances are, that .rect is orders of magnitude slower than all that destructuring. And then all that this achieves is complicating the code without any tangible benefit.
> Do you mean that when something like {:x 10} is used and the run-time value would be retrieved via (:x m), the inline version would just have 10 there, inlined?
Yes, that is exactly my question
Thanks for the detailed info!