Hello Everyone! I've been experimenting with the idea of performing compile-time partial evaluation of calls where possible, using a macro. I must preface this discussion that I realize it is a bit of an abuse of macros. The reasons for this abuse are 1) an attempt to mimic performance improvements created by C++ template metaprogramming, 2) to gain a more comprehensive understanding of CLojure, and 3) to create an example of a "two-level" language that has the same written form for both levels (compile time and run time). A lisp, which provides the opportunity to run code during the compilation phase, seems like the perfect place to introduce partial evaluation. Please feel free to inform me of previous attempts, gotcha's and the like. With that all out of the way, on to the problem:
Suppose I write a piece of code that has need for the 30th Fibb. number and we pretend that the FIbb function really has to be naive and recursive and slow: (defn fibb [n] (if (< n 2) n (+ (fibb (- n 1)) (fibb (- n 2))))) (defn my-func [x] (println "X + Fibb(30) is " (+ x (fibb 30)))) Now, you and I know there's no mutation funny-business going on here, and there's no dependency on any unknowns to calculate (fibb 30), but as you can see performing this experiment, Clojure and most other languages will leave (fibb 30) to be evaluated each time. And it should, because there /could/ be side-effects or I might want it to take a long time. What I would like is to define a macro which will evaluate (fibb 30) at compile time and then drop in the result: (defmacro par-eval [form] (eval form)) "But wait!" you say. That's poor form and besides, it's already defined as clojure.contrib.macros/const! Well, I want more than just evaluating a constant. I want to write my code as though it were all meant to be run at run-time, then wrap the code in my par-eval macro and have it automatically perform all the calculations that can be performed during compilation. Sort of a mix between c.c.macros/const and the c.c.template, probably using techniques from and c.c.macro-utils. I see two approaches, which seem to run into the same problem: 1) I can try to eval my form from the top and if I get an exception, I can try to eval the elements of form (if it is a seq), or 2) I can go to the leaves of form and try to eval them and percolate up either the result of evaluation, or the raw form if evaluation failed. -- For now, I am ignoring ns issues that might come into play -- Either approach appears to work for functions and produce a simplified, partially evaluated tree which is then dropped in place of the macro. Macros and special forms though cause some issues: 1) you must avoid processing their arguments blindly since they may represent something other than normal Clojure code and 2) you must perform variable substitution when entering a function or let or loop... To get symbol macros, it looks like Konrad Hinson had to resort to a reimplementation of the macro-expansion code and Stuart Sierra had to deal specially with all the different special forms as well in the template macros. It looks to me like I might be forced to perform my own special handling of special forms like 'if and 'let so that I can perform the correct analysis. As for macros, I think a call to eval should expand them for me, as would using macroexpand. Unlike const, this macro should not fail when eval does, it should just return the form unmolested. I haven't come to any strong conclusions about what to do about lexical variables referred to within the macro, but really I don't think there is anything that / can/ be done with them. If the code has side effects, then some may be performed during compilation and others may be left for run-time. You should only use the macro on purely functional expressions. Any ideas, comments, critiques would be appreciated! -- Tim --~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---