Attempt at rethrow macro

269 views
Skip to first unread message

Bill Robertson

unread,
Apr 1, 2013, 11:21:30 AM4/1/13
to clo...@googlegroups.com
I was all excited when I was able to consolidate a lot of try/catch logic behind a macro earlier this morning. All was good.

I felt like I could do a better job of communicating my intentions in the code though with a rethrow construct rather than writing 
    (catch FooException #f (throw #f))

I would have liked to have been able to simply write
    (rethrow FooException)

This failed. Poking around the docs a bit, I see that try/catch is a special form. Which makes sense.

user=> (defmacro rethrow [ex-class] `(catch ~ex-class x# (throw x#)))
#'user/rethrow
user=> (defmacro handle-ex [message & body] `(try ~@body (rethrow IllegalArgumentException) (catch Exception x# (throw (IllegalArgumentException. message)))))
#'user/handle-ex
user=> (handle-ex "no" (throw (IllegalArgumentException. "yes")))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: catch in this context, compiling:(NO_SOURCE_PATH:1:1)

It was a longshot, but I tried to qualify catch. That fails too, because it's not really there...

user=> (defmacro rethrow [ex-class] `(clojure.core/catch ~ex-class x# (throw x#)))
#'user/rethrow
user=> (defmacro handle-ex [message & body] `(try ~@body (rethrow IllegalArgumentException) (catch Exception x# (throw (IllegalArgumentException. message)))))
#'user/handle-ex
user=> (handle-ex "no" (throw (IllegalArgumentException. "yes")))
CompilerException java.lang.RuntimeException: No such var: clojure.core/catch, compiling:(NO_SOURCE_PATH:1:1)

Is this possible to do within the normal bounds of the language?

Thanks!


Ben Wolfson

unread,
Apr 1, 2013, 12:43:43 PM4/1/13
to clo...@googlegroups.com
IIRC "catch" is auxiliary syntax---it only has meaning within a (try ...) form.


--
--
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
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 



--
Ben Wolfson
"Human kind has used its intelligence to vary the flavour of drinks, which may be sweet, aromatic, fermented or spirit-based. ... Family and social life also offer numerous other occasions to consume drinks for pleasure." [Larousse, "Drink" entry]

Alf Kristian Støyle

unread,
Apr 1, 2013, 1:26:43 PM4/1/13
to clo...@googlegroups.com
Hey Bill.

I am guessing the problem is that the rethrow macro is expanded and passed to the reader/compiler before the handle-ex macro is. And at that point the compiler sees catch as a "standalone-symbol", not as part of the try special form. Macro-experts, please correct me :)

Tried to quickly catch up with http://www.infoq.com/presentations/Clojure-Macros, but infoq seems slow.

Cheers,
Alf


Alan Malloy

unread,
Apr 1, 2013, 2:49:29 PM4/1/13
to clo...@googlegroups.com
On Monday, April 1, 2013 10:26:43 AM UTC-7, Alf wrote:
I am guessing the problem is that the rethrow macro is expanded and passed to the reader/compiler before the handle-ex macro is. And at that point the compiler sees catch as a "standalone-symbol", not as part of the try special form. Macro-experts, please correct me :)

Tried to quickly catch up with http://www.infoq.com/presentations/Clojure-Macros, but infoq seems slow.


That is the opposite of the problem: `try` searches for a `catch` without macroexpanding its body forms. You could come at the problem differently, by writing a macro that expands to an entire try+catch form: (try-rethrowing [IllegalArgumentException] (do a b c) (catch Exception e e) (finally (foo))) is a macro that you could successfully write.

Armando Blancas

unread,
Apr 1, 2013, 3:07:41 PM4/1/13
to clo...@googlegroups.com
Define rethrow as a function; Alf's probably right. Also, change to: ~message.

user=> (defn rethrow [ex-class] `(catch ~ex-class x# (throw x#)))
#'user/rethrow
user=> 
user=> (defmacro handle-ex [message & body] 
  `(try ~@body ~(rethrow IllegalArgumentException) 
     (catch Exception x# (throw (IllegalArgumentException. ~message)))))
#'user/handle-ex
user=> 
user=> (handle-ex "no" (throw (IllegalArgumentException. "yes")))
IllegalArgumentException yes  user/eval9 (NO_SOURCE_FILE:7)

Bill Robertson

unread,
Apr 1, 2013, 3:20:19 PM4/1/13
to clo...@googlegroups.com
I think that's what is going on too. I tried quoting catch in the rethrow macro, but that didn't do it (didn't expect it to either).

(defmacro rethrow [ex-class] `('catch ~ex-class x# (throw x#)))

I still wonder if there is some sort of macrofoolery that would get it past the compiler. I'm not going to hold my breath though.

-Bill

Cedric Greevey

unread,
Apr 1, 2013, 4:00:31 PM4/1/13
to clo...@googlegroups.com
IMO, the real problem here is try not macroexpanding its body before looking for its catches. IMO that's a bug, and indeed that the rethrow macro doesn't work when the s-expression it expands to would work in its place represents a violation, at least in spirit, of homoiconicity. There are operators that are "special" and can't be supplied via macro. That's wrong.

Alan Malloy

unread,
Apr 1, 2013, 4:32:21 PM4/1/13
to clo...@googlegroups.com
This is how every macro and special form works. I know you like to complain, but the alternative is simply not possible: macros have complete control of expanding their bodies, and any macros therein are expanded later, not before. Try writing a macro system that goes the other way, and see how disastrous the result is.

Ben Wolfson

unread,
Apr 1, 2013, 4:37:52 PM4/1/13
to clo...@googlegroups.com
On Mon, Apr 1, 2013 at 1:32 PM, Alan Malloy <al...@malloys.org> wrote:
This is how every macro and special form works. I know you like to complain, but the alternative is simply not possible: macros have complete control of expanding their bodies, and any macros therein are expanded later, not before. Try writing a macro system that goes the other way, and see how disastrous the result is.

Precisely because "macros have complete control" of their bodies, it is not true that "any macros therein are expanded later, not before". A macro can, at its discretion, choose to macroexpand its arguments if it wants to. It's not always unreasonable to do this: I wrote a parallel let macro that expands the binding expressions in its binding vector to find out what names are occur free in them. That's much easier to do after fully macroexpanding the forms, since you only have to deal with the five or so primitive binding forms. (In that case the macroexpansion was only done for ease of investigating the forms, and the un-expanded versions were what were interpolated into the return value of the macro, but that's beside the point; a macro *can* expand its arguments.)

Bill Robertson

unread,
Apr 2, 2013, 12:20:07 AM4/2/13
to clo...@googlegroups.com
While it may violate the principle of least surprise (until you realize/learn that try/catch is a special form), I don't think it's a bug.

Cedric Greevey

unread,
Apr 2, 2013, 4:37:14 PM4/2/13
to clo...@googlegroups.com
On Tue, Apr 2, 2013 at 12:20 AM, Bill Robertson <billrob...@gmail.com> wrote:
While it may violate the principle of least surprise (until you realize/learn that try/catch is a special form), I don't think it's a bug.

Since there's no good engineering reason not to permit macros like (rethrow ...) to work, it is a bug. Also, pointing out that macros that can break this property is something of a straw man when I'm indicating that something *built into the language* has the problem. The latter can be a language fault even if the former is not.

Reply all
Reply to author
Forward
0 new messages