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

Reply via email to