This might be a silly question, but:
Suppose an expression depends only on literals, or on other expressions that also only depend on literals; will the compiler evaluate this at compile time?
Suppose I have,
(def a (some-time-consuming-function some-literal))
(def b (some-other-time-consuming-function a))
Will both b and a be evaluated completely at compile time, so that the User isn't affected?
EDIT: Thanks very much, all of the answers were very helpful.
EDIT 6.6.2011: Turns out that if you try and use this technique to precompute a very large data structure, it's easy to make class files that are too big to load. In those cases you want to create a file which will be read in rather than a class file that will be loaded. The macro tricks described in these answers should only be applied as is in situations where the return value is not a prohibitively large structure.
The error thrown is: "java.lang.ClassFormatError: Invalid this class index" See this thread for discussion of a related situation.
Not silly at all, I had to think about it and test it out.
It will work only if you use macros instead of functions, since the body of a macro is evaluated at compile/macroexpansion-time. E.g.:
(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 to the values returned from evaluating preprocess
.
Firstly, there is a pretty important reason why the right-hand-sides of def
s need to be evaluated at load time: they might depend on the environment somehow and in the general case it's impossible to tell whether they do or not. Take e.g.
(def *available-processors* (.availableProcessors (Runtime/getRuntime)))
Secondly, here's one approach to testing what actually happens:
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 [v e]
(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
; surely enough, the code takes its time twice.
Run java -jar testdefs-1.0.0-SNAPSHOT-standalone.jar
; you'll notice the code only takes its time once.
In your example, the time-consuming functions are called only once, when your code is loaded.
The Clojure compiler does not try to optimize constant expressions, but the Java JIT compiler may do so in some cases.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With