Hi!

Yesterday on IRC some of us were struggling with the usage of
dynamically rebound vars inside macros which led to interesting
compile vs run time issues. I've got some questions for the experts at
the very bottom of this post - but before I'd like to explai this
issue for the less experienced macrologists amongst us =)

Let's start with the definition of assert in clojure.core:

(defmacro assert
  "Evaluates expr and throws an exception if it does not evaluate to
 logical true."
  [x]
  (when *assert*
    `(when-not ~x
       (throw (new AssertionError (str "Assert failed: " (pr-str
'~x)))))))

This way setting *assert* to false makes (assert anything) expand to
nil and thus gets rid of all assert statements. This might be useful
to speed up stable but time-critical code in "production mode" without
having to manually remove all assertments. Similar behaviour might be
interesting for debugging output.

So lets say we want to have a macro in-production-mode which used
*assert* to "turn off" assert statements:

(in-production-mode (assert nil))
=> nil

But how can we define this macro? My first idea would have been this:

(defmacro in-production-mode [& body]
   `(binding [*assert* false]
      ~...@body))

But this does not work - the inner assert is still being expanded -
thus giving an exception when used for the example above. This is due
to the difference between compile time and run time in Lisps. That is,
the inner assert is already being macroexpanded at compile time while
the binding only happens at run time when it's too late:

compile time:

(in-production-mode (assert false))
=>
(binding [*assert* false]
   (assert nil))
=>                                                 *assert* still true
here
(binding [*assert* false]
  (when-not nil
     (throw ...)))

So we want to make the rebinding happen during compilation. How about
that:

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

Doesn't work either - let's look at the steps again:

compile time:

(in-production-mode (assert false))
=>                                   *assert* false at this point
(do (assert nil))
=>                                   *assert* true again - we are no
longer in the scope of the call toin-production-mode
(do (when-not nil
       (throw ...)))

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)

*assert*
=> true

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.

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.
So my questions are:

- Is there a better way to define in-production-mode?
- How was *assert* meant to be used?
- 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?

Cheers and thanks for your answers,
Benjamin

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