Weird nested macro problem

91 views
Skip to first unread message

Straszheim, Jeff

unread,
Feb 1, 2011, 4:09:16 PM2/1/11
to clo...@googlegroups.com
So, I have a macro that looks something like this:

(defmacro test-failure
[& forms]
`(handler-case :type
~@forms
(~'handle :error/error
(println "error happened"))))


(My real macro is more complex, but this gives the idea.)

If I eval

(test-failure (println "test"))

I get:

=> Unable to resolve symbol: handle in this context

However, if I macroexpand-1 the thing, I get:

(macroexpand-1 '(test-failure (println "test")))

=> (clojure.contrib.condition/handler-case :type (println "test") (handle :error/error (clojure.core/println "error happened")))

That is correct, and if I eval just that it works fine.

Here is an interesting thing: if I do this:

(macroexpand '(test-failure (println "test")))

I get

=> (try (println "test") (handle :error/error (clojure.core/println "error happened")) (catch clojure.contrib.condition.Condition c__19__auto__ (clojure.core/binding [clojure.contrib.condition/*condition-object* c__19__auto__ clojure.contrib.condition/*condition* (clojure.core/meta c__19__auto__) clojure.contrib.condition/*selector* (:type (clojure.core/meta c__19__auto__))] (clojure.core/cond :else (clojure.contrib.condition/raise)))))

It's expanded the handler-case part, but the "handle" is still there. That seems wrong to me.

I've tried lots of different ways to specify handle besides ~'handle, but they all seem to fail.

Any suggestions?

jweiss

unread,
Feb 1, 2011, 4:49:46 PM2/1/11
to Clojure
If I remember right from looking at clojure.contrib.condition's source
(which I did because I wrote a similar error handling lib, which has a
few extra features but isn't ready for prime time)...

"handle" doesn't actually exist as a function or macro. It doesn't
expand - the handler-case macro looks for it and basically drops the
symbol "handle" and uses the rest of the form to create a function. I
would think the unquote-quote should leave "handle" unqualified and do
the right thing, but apparently not.

https://github.com/richhickey/clojure-contrib/blob/ec6a7579d6b1c0bfa42e3666cfad196cffc966fe/src/main/clojure/clojure/contrib/condition.clj#L71

I believe somehow the (= 'handle (first %)) comparison on line 92 is
returning false on your handle form. I am not sure why. But that
might be worth exploring at the repl.

Jeff

Meikel Brandmeyer

unread,
Feb 1, 2011, 5:31:26 PM2/1/11
to clo...@googlegroups.com
Hi,

the failing part is actually not the comparison of the symbols, but the check for listness.

user=> (list? (nth `(handler-case :type (println "test") (~'handle foo)) 3))
false
user=> (seq? (nth `(handler-case :type (println "test") (~'handle foo)) 3))
true

Sincerely
Meikel

George Jahad

unread,
Feb 2, 2011, 1:48:04 AM2/2/11
to Clojure
As usual, Meikel has the right answer. But I didn't quite get it at
first.

It looks like syntax-quote generates cons's, not lists:
user> (type (nth `(handler-case :type (println "test") (~'handle foo))
3))
clojure.lang.Cons

Your macroexpand-1 example worked because the reader doesn't
distinguish between cons's and lists.

Because handler-case is expecting a list and syntax-quote won't
generate one, you'll have to write your macro like this:
(defmacro test-failure
[& forms]
`(handler-case :type
~@forms
~(list 'handle :error/error
'(println "error happened"))))

Finally, a shameless plug: you could have used the cdt, or debug-repl
to set a break around line 92 and figure it out yourself. That's what
I did.

Ken Wesson

unread,
Feb 2, 2011, 2:44:21 AM2/2/11
to clo...@googlegroups.com
On Wed, Feb 2, 2011 at 1:48 AM, George Jahad
<clo...@blackbirdsystems.net> wrote:
> As usual, Meikel has the right answer.  But I didn't quite get it at
> first.
>
> It looks like syntax-quote generates cons's, not lists:
> user> (type (nth `(handler-case :type (println "test") (~'handle foo))
> 3))
> clojure.lang.Cons
>
> Your macroexpand-1 example worked because the reader doesn't
> distinguish between cons's and lists.
>
> Because handler-case is expecting a list and syntax-quote won't
> generate one

This also means that macros should not use list? to test whether an
object is a nonatomic s-expression. Unfortunately core doesn't contain
a compact test for atomicity; to get all the list-y things that print
as (foo bar baz ...) you can use something like

(and
(coll? x)
(not (or (vector? x) (map? x) (set? x)))

which should return logical true only when x is list-y. Wrap that in a
predicate function and use it in your macros in place of list?.

Meikel Brandmeyer

unread,
Feb 2, 2011, 3:22:39 AM2/2/11
to Clojure
Hi,

On 2 Feb., 08:44, Ken Wesson <kwess...@gmail.com> wrote:

> This also means that macros should not use list? to test whether an
> object is a nonatomic s-expression. Unfortunately core doesn't contain
> a compact test for atomicity; to get all the list-y things that print
> as (foo bar baz ...) you can use something like
>
> (and
>   (coll? x)
>   (not (or (vector? x) (map? x) (set? x)))
>
> which should return logical true only when x is list-y. Wrap that in a
> predicate function and use it in your macros in place of list?.

seq? should just do fine, since lists are their own seq. coll? just
checks for IPersistentCollection. Why should that hint to list-y
printing?

All in all, this casts another question mark on this style of
programming, IMHO.

Sincerely
Meikel

Straszheim, Jeff

unread,
Feb 2, 2011, 10:36:06 AM2/2/11
to clo...@googlegroups.com
Thanks. That is indeed what fixed it!

And macroexpand (and pr in general) should have an option to mark what is a list and what is a cons thingy. That is confusing.

> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@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+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en

Ken Wesson

unread,
Feb 2, 2011, 1:00:18 PM2/2/11
to clo...@googlegroups.com
On Wed, Feb 2, 2011 at 3:22 AM, Meikel Brandmeyer <m...@kotka.de> wrote:
> Hi,
>
> On 2 Feb., 08:44, Ken Wesson <kwess...@gmail.com> wrote:
>
>> This also means that macros should not use list? to test whether an
>> object is a nonatomic s-expression. Unfortunately core doesn't contain
>> a compact test for atomicity; to get all the list-y things that print
>> as (foo bar baz ...) you can use something like
>>
>> (and
>>   (coll? x)
>>   (not (or (vector? x) (map? x) (set? x)))
>>
>> which should return logical true only when x is list-y. Wrap that in a
>> predicate function and use it in your macros in place of list?.
>
> seq? should just do fine, since lists are their own seq. coll? just
> checks for IPersistentCollection. Why should that hint to list-y
> printing?

Combining coll? with (not (or (vector?) (map?) (set?))) eliminates the
other three types of coll and leaves seqs and lists.

Oddly, I seem to remember having had to work around a lack of seq?
previously, yet it says on the API page that it's been there since
1.0. Or perhaps it was the larger problem of determining if something
will work with (seq x) that was lacking built-in API...

It does indeed seem that seq? on lists returns true, so it looks like
you can just use that instead of list? in macros to check for (foo
...) s-expressions.

Reply all
Reply to author
Forward
0 new messages