Let forms: How to access destructed characters in a macro?

I am trying to write a macro that expands to a let form with destructuring. My problem is that I would like to have a list of characters that are defined in the let form, including those that were obtained by destruturing.

Use case

I am trying to discard this behavior, for example, to check:

(let [a (foo bar) {x :x, y :y, {u :u, v: v :as nested-map} :nested} some-map] (and xy nested-map uv ; testing truthiness (valid-a? a) (valid-x? x) (valid-y? y) (valid-nested? nested-map) (valid-u-and-v? uv) )) 

Proposed solution

It would be very nice to achieve this with a kind of and-let macro, which I could name as follows:

 (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? uv)) 

What am i missing

But I don’t have any way to access the list of characters that are connected 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 understood how I can do this?

+6
source share
2 answers

Destructuring is handled by the clojure.core/destructure function. It is publicly available, so we can call it ourselves and extract the names of all local residents, including the names indicating the intermediate results used in the destruction:

 (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 [bc] [2 3]] (nil? foo))) ;= true 
+5
source

You can do most of this with a function that is directly connected to the map.

 (defn validate [vm ; validation map dm ; data map ] (and (every? identity (map #(% dm) (flatten (keys vm)))) (every? identity (map (fn [[k vf]] (if (vector? k) (apply vf (map #(% dm) k)) (vf (k dm)))) vm)))) 

for instance

 (validate {:s string?, :n number? :m number? [:m :n] > } {:s "Hello!", :m 5, :n 3}) ; true (validate {:s string?, :n number? :m number? [:m :n] > } {:s "Hello!", :m 5, :n 6}) ; false (validate {:s string?, :n number? :m number? :u \b [:m :n] > } {:s "Hello!", :m 5, :n 6}) ; false 

You can add any extraneous variables, a to your example, to the map in advance. This, unnecessarily, validates a . No harm?

0
source

All Articles