I'm trying to write a macro which expands to a let form with destructuring. My problem is that I would like to have the list of the symbols that are defined in the let form, including those obtained by destruturing.
Use case
I'm trying to factor out this kind of behavior, for validation for example :
(let [a (foo bar)
{x :x,
y :y,
{u :u, v: v :as nested-map} :nested} some-map]
(and x y nested-map u v ; testing truthiness
(valid-a? a)
(valid-x? x)
(valid-y? y)
(valid-nested? nested-map)
(valid-u-and-v? u v)
))
Proposed solution
It would be really nice to achieve this through some sort of and-let
macro which I could call like this:
(and-let [a (foo bar)
{x :x,
y :y,
{u :u, v: v :as nested-map} :nested} some-map]
(valid-a? a)
(valid-x? x)
(valid-nested? nested-map)
(valid-u-and-v? u v))
What I'm missing
But I'm missing some way of accessing the list of the symbols that are bound in the let form. If I had something like a list-bound-symbols
function, I could do it like this :
(defmacro and-let
"Expands to an AND close that previouly checks that the values declared in bindings are truthy, followed by the tests."
[bindings & tests]
(let [bound-symbols (list-bound-symbols bindings) ;; what I'm missing
]
`(let ~bindings
(and
~@bound-symbols
~@tests)
)))
Has anyone got a clue how I might do this?
Destructuring is handled by the clojure.core/destructure
function. It's public, so we can call it ourselves and extract the names of all locals, including those naming intermediate results used in destructuring:
(defmacro and-let [bindings & tests]
(let [destructured (destructure bindings)]
`(let ~destructured
(and ~@(take-nth 2 destructured)
~@tests))))
Seems to work:
(let [foo nil]
(and-let [a 1
[b c] [2 3]]
(nil? foo)))
;= true
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With