Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Clojure evaluates forms during AOT compilation?

I've set up small project:

project.clj:

(defproject testing-compilation "0.1.0-SNAPSHOT"
  :dependencies [[org.clojure/clojure "1.8.0"]]

  ;; this is important!
  :aot :all)

src/core.clj

(ns testing-compilation.core)

(def x (do
         (println "Print during compilation?")
         1))

Then when I do lein compile in project directory I'm seeing output from a print:

$ lein compile
Compiling testing-compilation.core
Print during compilation?

My question is: why clojure evaluates top level forms during AOT compilation? Shouldn't they be evaluated at program startup?

For reference, Common Lisp doesn't evaluate forms by default and provides ability to tune this behaviour. Anything similar in Clojure? If nothing, does Clojure documentation explicitly state such behaviour?

UPD: Forms are evaluated at startup as well.

After specifying a main namespace and writing main function that prints Hello, world!, I did this:

$ lein uberjar
Compiling testing-compilation.core
Print during compilation?
Created testing-compilation-0.1.0-SNAPSHOT.jar
Created testing-compilation-0.1.0-SNAPSHOT-standalone.jar

$ java -jar target/testing-compilation-0.1.0-SNAPSHOT-standalone.jar
Print during compilation?
Hello world!
like image 744
OlegTheCat Avatar asked Jul 27 '16 16:07

OlegTheCat


People also ask

What is the Clojure compilation model?

The Clojure compilation model preserves as much as possible the dynamic nature of Clojure, in spite of the code-reloading limitations of Java. Source and classfile pathing follows Java classpath conventions.

Why should I use AOT compilation?

Some reasons to use AOT compilation are: To create an application that does not need runtime bytecode generation and custom classloaders The Clojure compilation model preserves as much as possible the dynamic nature of Clojure, in spite of the code-reloading limitations of Java.

What is ahead of time (AOT) compilation?

Clojure compiles all code you load on-the-fly into JVM bytecode, but sometimes it is advantageous to compile ahead-of-time (AOT). Some reasons to use AOT compilation are: To create an application that does not need runtime bytecode generation and custom classloaders

How do you evaluate operators in Clojure?

Both the operator and the operands (if any) are evaluated, from left to right. The result of the evaluation of the operator is cast to IFn (the interface representing Clojure functions), and invoke () is called on it, passing the evaluated arguments.


1 Answers

The first part of the AOT process is to find the file containing the main namespace and load it by evaluating every expression from top to bottom.

Some of these expressions will be require expressions that will have the effect of loading other namespaces, which recursively load yet more namespaces.

Other will be defn expressions that will have the effect of firing up the compiler and producing class files. One class file is produced for each function.

Other expressions may do some calculation and then do things that produce class files, so it's important to give them a chance to run. Here's a made up example:

user> (let [precomputed-value (reduce + (range 5))]
        (defn funfunfun [x]
          (+ x precomputed-value)))
#'user/funfunfun
user> (funfunfun 4)
14

It is possible to design a lisp that would not evaluate top level forms at start or, as you mention, make it optional. In the case of Clojure it was decided to keep a single evaluation strategy across both AOT and "non AOT" loading so programs always run the same regardless of how they are compiled. These are personal design choices made by others so I can't speak to their motivations here.

like image 68
Arthur Ulfeldt Avatar answered Oct 03 '22 10:10

Arthur Ulfeldt