I tried to solve this, and, having very litte macro-writing experience, just looked at when-let and tried to modify it to work with multiple binds. This is what I ended up with:
(defmacro when-lets [bindings & body] (if-not (seq bindings) `(do ~@body) (let [form (bindings 0) tst (bindings 1) rst (drop 2 bindings)] `(let [temp# ~tst] (when temp# (let [~form temp#] (when-lets ~(vec rst) ~@body))))))) Not as elegant as Dmitry's code, I know... But before I landed on the above version, I had this: (defmacro when-lets [bindings & body] (if-not (seq bindings) `(do ~@body) (let [form (bindings 0) tst (bindings 1) rst (drop 2 bindings)] `(let [temp# ~tst] (when temp# (when-lets ~(vec rst) (let [~form temp#] ~@body))))))) Notice that the when-lets and let close to the end there has swapped places. This is wrong, because the tests/values get evaluated in the wrong order. But apart from that I expected this to work. But it does not. This last code yields this: => (when-lets [a 1 b 2] [a b]) [2 2] Two questions: First: Why doesn't macroexpand expand the inner when-lets? => (macroexpand '(when-lets [a 1 b 2] [a b])) (let* [temp__1551__auto__ 1] (clojure.core/when temp__1551__auto__ (user/when-lets [b 2] (clojure.core/let [a temp__1551__auto__] [a b])))) Second: If I extract the inner when-lets manually and expand that, and reinsert it, I end up with this: (let* [temp__1551__auto__ 1] (clojure.core/when temp__1551__auto__ (let* [temp__1551__auto__ 2] (clojure.core/when temp__1551__auto__ (user/when-lets [] (clojure.core/let [b temp__1551__auto__] (clojure.core/let [a temp__1551__auto__] [a b]))))))) I thought that the name of the gensyms might just be the same for a and b because I did two expands in the repl or something. But it seems to me that them getting the same name is the only explaination for the result above (maybe "they" are the same, somehow?). Is the gensym in the two expands the same thing, or do "they" get the same name? That was surprising to me. I can't think of any real example where that is a problem. But what if I had wanted to write a macro like the second when-lets? On Jul 27, 4:51 pm, Dmitry Gutov <raa...@gmail.com> wrote: > This would be a straightforward solution: > > (defmacro when-lets [bindings & body] > `(let ~bindings > (when (and ~@(map first (partition 2 2 bindings))) > ~@body))) > > It works well in simple cases, but breaks e.g. in case of parameter > destructuring. > If you read `(source when-let)`, you'll see that it uses a temporary > binding. So we can add that and a loop, or just reuse `when-let` and > use something akin to recursion: > > (defmacro when-lets [bindings & body] > (if (empty? bindings) > `(do ~@body) > `(when-let [~@(take 2 bindings)] > (when-lets [~@(drop 2 bindings)] > ~@body)))) > > user=> (when-lets [a 1 [b c] [1 2]] (+ a b c)) > 4 > user=> (when-lets [a 1 [b c] nil] (+ a b c)) > nil > > Questions? > > On Jul 27, 4:50 pm, Feng Shen <shen...@gmail.com> wrote: > > > > > > > > > Clojure core.clj has a macro when-let, > > I am wondering how to write a macro `when-lets` > > > (when-lets [symbol-1 test-1 > > symbol-2 test-2 > > ... > > ] > > body > > ) > > > body only get evaluated when (and test-1 test-2 ....) > > I am thinking about it, anybody has any clue? -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en