On 7 Mar 2010, at 21:03, Benjamin Teuber wrote:

Another idea would be to alter the root binding of *assert* instead. I
guess this solution would work for your own vars, but unfortunately it
is not possible to do this with *assert*:

(def *assert* false)

You cannot use def to change the value of a var in another namespace. This is nothing specific to *assert* or to clojure.core.

Here is what you can do instead:

        (alter-var-root (var *assert*) (fn [_] false))

However, what you probably want is

        (set! *assert* false)

This changes the thread-local value of *assert*, not the root binding. Contrary to binding, set! makes a permanent change valid for the rest of the thread's lifetime (or until another set!). Changing the root value of *assert* affects other threads doing macro expansion in parallel. At the moment the Clojure compiler doesn't do parallel compilation, but perhaps one day it will. There could also be application code calling eval and thus macro expansion in another thread, which could fail because of your modifications to *assert*. So better use set!.

I guess users are not allowed to alter root bindings for vars in
clojure.core at all - but it would be better to get an exception
instead of this behaviour - I might submit a ticket for this.

An exception would indeed make sense here.

So, finally, I have come up with this:

(defmacro in-production-mode [& body]
  (binding [*assert* false]
     `(do ~@(clojure.walk/macroexpand-all body))))

But I feel bad about explicitly calling macroexpand - smells like
directly calling eval to me.

First of all, please don't use clojure.walk/macroexpand-all at all if you care about faithful macro expansion. It doesn't treat special forms specially, so what you get is not always equivalent to how the compiler expands macros.

You can use clojure.contrib.macro-utils/mexpand-all, which does treat special forms correctly - at the moment. It has special routines for special forms, which have to be kept up to date as Clojure evolves. I think they are correct for the 1.2 master branch, and I try to keep them up to date of course, but there is always the risk of a delay.

Looking at clojure.contrib.macro-utils, you will see other macros that do a full macro expansion of their bodies: macrolet and symbol- macrolet. There is no other way to do this, so I don't feel bad about it, but like eval this should be done only when inevitable.

- How was *assert* meant to be used?

The normal use is module-wide, by putting

        (set! *assert* false)

right after the ns form. This compiles the whole namespace without assertions.

- Is it okay to call macroexpand-all on arbitrary code? Or can
anything bad happen if the author of that code is not aware of this
fact?

If done correctly, it should never cause trouble. Nothing in the language specification says when macroexpansion happens. The Clojure compiler could do a full expansion and then compile. I have substantial code using symbol-macrolet in use, but the implicit full macro expansion has never caused a single bug.

The undesirable side effect of full macro expansion is that it makes debugging more difficult. A single macroexpand-1 on your in-production- mode macro will do a full macro expansion, resulting in code that its original author might not recognize.

Konrad.

--
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