Will the clojure compiler automatically evaluate literal expressions at compile time?

This may be a dumb question, but:

Suppose an expression depends only on literals or on other expressions that also depend only on literals; will the compiler evaluate this at compile time?

Suppose I,

(def a (some-time-consuming-function some-literal)) (def b (some-other-time-consuming-function a)) 

Will both b and a be fully evaluated at compile time so that the user does not suffer?

EDIT: Thank you very much, all the answers were very helpful.

EDIT 6.6.2011: It turns out that if you try to use this technique to precompute a very large data structure, it is easy to make class files that are too large to load. In these cases, you want to create a file that will be read, not a class file that will be loaded. The matrix tricks described in these answers should only be used when the return value is not an overly large structure.

Error issued: "java.lang.ClassFormatError: Invalid index for this class" See this thread for a discussion of the related situation.

+7
compiler-construction clojure
source share
4 answers

Not stupid at all, I had to think about it and check it out.

It will only work when using macros instead of functions, since the body of the macro is evaluated at the time of compilation / macro expansion. For example:.

 (defmacro preprocess [f & args]
   (let [x # (apply (resolve f) args)]
     `~ x #))

 (def a (preprocess some-time-consuming-function some-literal))

 (def b (preprocess some-other-time-consuming-function a))

Then a and b are def 'd for the values ​​returned by the preprocess evaluation.

+6
source share

In your example, laborious functions are called only once when your code is loaded.

The Clojure compiler does not attempt to optimize constant expressions, but the Java JIT compiler can do this in some cases.

+3
source share

Firstly, there is a rather important reason why the right-hand sides of def should be evaluated at boot time: they may somehow depend on the environment, and in the general case it is impossible to say whether they do or not. Take for example

 (def *available-processors* (.availableProcessors (Runtime/getRuntime))) 

Secondly, here is one approach to checking what is actually happening:

  • Create a test project with Leiningen - say lein new testdefs .

  • Put :main testdefs.core in project.clj .

  • Put the following in src/testdefs/core.clj :

     (ns testdefs.core (:gen-class)) (defn take-your-time [t] (printf "Taking my time (%d)...\n" t) (Thread/sleep t)) (def a (take-your-time 5000)) (defmacro frozen-def [ve] (let [val (eval e)] `(def ~v ~val))) (frozen-def b (take-your-time 5000)) (defn -main [& args] (println "Starting...") (println a) (println b)) 
  • Run lein uberjar ; Of course, the code takes its time twice.

  • Run java -jar testdefs-1.0.0-SNAPSHOT-standalone.jar ; You will notice that the code takes its time only once.

+3
source share

In the above examples, expressions are evaluated at the time of compilation / loading. def always evaluates its second argument. Of special shapes

 (def symbol init?) 

Creates and internships or finds a global var with the name symbol and the namespace of the value of the current namespace *ns* . If init supplied, it is evaluated , and the root binding of the var variable is set to the resulting value.

0
source share

All Articles