I've been experimenting with the following patches to
clojure.lang.Compiler and clojure.lang.RT. They allow a callback to a
clojure function when the compiler compiles a fn.
With the patches you can, for example:
(in-ns 'clojure)
(defn *fncompile-listener*)
[name form] (println name form))
(in-ns 'user)
(clojure/refer 'clojure)
(defn myfunc
[s] (map (fn[a] (* a 2)) s))
with the following output:
user.myfunc__4659$fn__4661 (fn* ([a] (* a 2)))
user.myfunc__4659 (fn* ([s] (map (fn [a] (* a 2)) s)))
The intent is to be able to capture the generated class names and
associated forms for reference when examining stack traces, profiling
information, and perhaps other uses.
Note that using the binding macro for *fncompile-listener* typically
won't give the desired result, since the inner forms are compiled
before the binding takes effect, but binding can be used in cases
where it is applied to a function that evals, eg
(binding [*fncompile-listener* (fn[a b] (println "[fncompile]" a b))]
(eval '(map (fn[a] (* a 2)) [1 2 3])))
gives the following:
user.eval__4779$fn__4781 (fn* ([a b] (println [fncompile] a b)))
user.eval__4779 (fn* ([] (binding [*fncompile-listener* (fn [a b]
(println [fncompile] a b))] (eval (quote (map (fn [a] (* a 2)) [1 2
3]))))))
[fncompile] user.eval__4785$fn__4787 (fn* ([a] (* a 2)))
[fncompile] user.eval__4785 (fn* ([] (map (fn [a] (* a 2)) [1 2 3])))
Although this binding example is a bit awkward, I'm able to use
binding with *fncompile-listener* in a clean way in my GUI REPL, since
I have an evaluator function that I can wrap easily.
Rich - please consider incorporating these patches (or a similar
capability). Thanks.
Index: C:/Documents and Settings/MESSINM2/My Documents/workspace/
clojure/src/jvm/clojure/lang/Compiler.java
===================================================================
--- C:/Documents and Settings/MESSINM2/My Documents/workspace/clojure/
src/jvm/clojure/lang/Compiler.java (revision 957)
+++ C:/Documents and Settings/MESSINM2/My Documents/workspace/clojure/
src/jvm/clojure/lang/Compiler.java (working copy)
@@ -192,6 +192,9 @@
//DynamicClassLoader
static final public Var LOADER = Var.create();
+//mm 23jul08 - experimental listener for fn compile
+static final public Var FNC_LISTENER =
Var.find(Symbol.create("clojure","*fncompile-listener*"));
+
enum C{
STATEMENT, //value ignored
EXPRESSION, //value required
@@ -2758,6 +2761,10 @@
Var.popThreadBindings();
}
fn.compile();
+ //mm 23jul08 experimental fn listener
+ if(FNC_LISTENER.get()!=null)
+ ((IFn)FNC_LISTENER).invoke(
fn.name,form);
+
return fn;
}
Index: C:/Documents and Settings/MESSINM2/My Documents/workspace/
clojure/src/jvm/clojure/lang/RT.java
===================================================================
--- C:/Documents and Settings/MESSINM2/My Documents/workspace/clojure/
src/jvm/clojure/lang/RT.java (revision 957)
+++ C:/Documents and Settings/MESSINM2/My Documents/workspace/clojure/
src/jvm/clojure/lang/RT.java (working copy)
@@ -142,6 +142,7 @@
final static Var PRINT_READABLY = Var.intern(CLOJURE_NS,
Symbol.create("*print-readably*"), T);
final static Var WARN_ON_REFLECTION = Var.intern(CLOJURE_NS,
Symbol.create("*warn-on-reflection*"), F);
+final static Var FNC_LISTENER = Var.intern(CLOJURE_NS,
Symbol.create("*fncompile-listener*"), null);
//final static Var IMPORTS = Var.intern(CLOJURE_NS,
Symbol.create("*imports*"), DEFAULT_IMPORTS);
final static IFn inNamespace = new AFn(){
public Object invoke(Object arg1) throws Exception{