Error formatting macro: pagetree: java.lang.NullPointerException
Skip to end of metadata
Go to start of metadata
You are viewing an old version of this page. View the current version. Compare with Current  |   View Page History

Problem Statement

It is easy to change the values of compile-time vars like *warn-on-reflection* and *unchecked-math* from a particular point in a file until the end of the file with set!.  You can also change the value of one of these vars for one top-level form by setting it to a desired value before the top-level form, and then setting its value back to the original after that top-level form.

However, there are cases where it would be desirable to change the value of such a var for an individual expression.

  1. For example, you may want *unchecked-math* to be true for part of a function's definition, either a few individual subexpressions, or a larger part of it.
  2. Another is that you want *warn-on-reflection* to be true for most of your library, but there are a few instances of reflection that you are aware of and either they cannot be eliminated, or they are rare enough in practice that they are not a performance problem.  You want to mark the expressions using reflection so that they do not cause a compile-time warning, but you want such warnings enabled everywhere else in that same function in case future changes introduce a new use of reflection.

Proposal A: new special form compile-time-let

Introduce a new special form in Clojure called compile-time-let that takes a vector of bindings, and makes those bindings active in the textual scope of the compile-time-let expression during compile time.  For example:

;; I know it is easy to eliminate reflection in this simple example.
;; Imagine a case where it was impossible or undesirable to eliminate reflection.

(defn recip [x]
(if (ratio? x)
(compile-time-let [*warn-on-reflection* false]
(/ (.denominator x) (.numerator x)))
(/ 1 x)))

The following macro known-reflection could also be added to shorten disabling *warn-on-reflection*, if that became a common case:

(defmacro known-reflection [& body]
`(compile-time-let [~'*warn-on-reflection* false]
~@body))

With such a macro the definition of recip above could be rewritten:

(defn recip [x]
(if (ratio? x)
(known-reflection (/ (.denominator x) (.numerator x)))
(/ 1 x)))

A proof-of-concept implementation of compile-time-let for Clojure on the JVM has been added as an attachment patch1.txt to this page.  Select "Attachments" under the Tools menu near the top right of the page to see it.

Proposal B: metadata annotation on code

This suggested syntax is from Aaron Cohen:

(defn recip [x]
(if (ratio? x)
^{:warn-on-reflection false} (/ (.denominator x) (.numerator x))
(/ 1 x)))
^:warn-on-reflection ^:unchecked-math (+ 1 2)

The macro known-reflection from proposal A above can be implemented as follows with this idea.  There may be shorter ways, too, but this one is tested to work.

(defmacro known-reflection [& body]
`(do
^{:warn-on-reflection false}
(do ~@body)))

In case you are curious, this implementation of known-reflection does not work:

(defmacro known-reflection [& body]
^{:warn-on-reflection false}
`(do ~@body))

For this idea to work as most Clojure programmers would expect, CLJ-865 (http://dev.clojure.org/jira/browse/CLJ-865) would need to be fixed so that metadata can annotate subexpressions that are macros, e.g. let, loop, case, and many other common subexpressions.  CLJ-865's patch updated.patch dated June 1, 2012 has been tested with a proof-of-concept implementation of this idea, and seems to work.  See patch metadata-patch1.txt by selecting "Attachments" under the Tools menu near the top right of the page.

Question: Should subexpression-level modification of * compiler-options * be included in this as well, e.g. by implementing the proper behavior for keywords :elide-meta and :disable-locals-clearing?

 

Discussions on the clojure-dev group:

Thread with subject "Proposal for *warn-on-reflection* to be true by default" begun on Feb 25, 2012:

http://groups.google.com/group/clojure-dev/browse_frm/thread/5053bcb07ab33261

Thread with subject "Arbitrary subexpressions with custom compiler flags" begun on Mar 20, 2012:

http://groups.google.com/group/clojure-dev/browse_frm/thread/d14692f8c825d27f

Labels: