liveBook · Manning

Ahead-of-time compilation

As explained in Clojure: The Essential Reference

Ahead-of-time compilation is compilation performed before program execution to produce an executable or loadable representation.

Ahead-of-time (AOT) compilation runs Clojure compilation before the application starts and caches the result as class files on disk. Use `compile` on a namespace, or invoke `clojure.lang.Compile` from the command line. Generated files go under `*compile-path*` (by default, `classes`); when both class files and sources are present, the class files take precedence. section 17.4

Choose AOT when its deployment benefits outweigh its constraints:

  • It can reduce startup time, avoid distributing Clojure sources, and make generated classes available to Java and other JVM languages. section 17.4
  • Generated classes are tied to a particular Clojure-JVM version. Builds must track which sources need recompilation, and tests can miss problems in code they never evaluate. section 17.4

Compile a namespace

Given a classpath-visible `source.clj` containing `(ns source)` and `(defn plus [x y] (+ x y))`, evaluate `(compile 'source)`; it returns `source` and writes class files for the namespace and its functions under `*compile-path*`. The generated classes can then be found through the classpath. section 17.4

For Java-facing entry points, `gen-class` works with AOT compilation to put a physical class on disk. Declaring `:main true` alone is insufficient: the associated namespace must also define `-main`, or calling the generated `main` fails. A `gen-class` call made directly in an already-running REPL does not generate the class; compile a file containing the declaration instead. `deftype`, by contrast, can generate types without AOT, though AOT can export them for Java use. section 15.3 section 15.4

(spit (str *compile-path* "/bookgenclass.clj")
  "(ns bookgenclass)

   (gen-class :name book.GenClass
              :main true)

   (defn -main [& args] ; #1
     (println \"Hello from Java!\"))")

(compile 'bookgenclass) ; #2

(GenClass/main (make-array String 0)) ; #3
;; Hello from Java!

Compile a namespace that declares a Java `main` class and implements `-main`; calling the generated method prints `Hello from Java!`. section 15.3

Compilation time is also an evaluation boundary. `compile` binds `*compile-files*` to true, so initialization that depends on the deployment machine can be guarded until runtime; `delay` is another option but requires an explicit dereference. Compile-time bindings can change generated behavior: binding `*assert*` to false while compiling disables those assertions, and binding `clojure.test/*load-tests*` to false prevents colocated test functions from being generated. section 23.6 section 23.4

;; export CLOJURE_JAR=~/.m2/repository/org/clojure/clojure/1.8.0/clojure-1.8.0.jar ; #1
;; java -cp $CLOJURE_JAR:. clojure.main

(spit "math.clj" ; #2
"(ns math (:require [clojure.test :as t]))
 (defn sum [a b] (+ a b))
 (t/deftest sum-test (t/is (= 2 (sum 1 1))))")

(require '[clojure.test :as t])
(binding [*compile-path* "."
          t/*load-tests* false] ; #3
  (compile 'math))
;; math

The `math` example binds `*compile-path*` and `clojure.test/*load-tests*` around `compile`, producing a deployment build without its colocated test. section 23.14

AOT is therefore a deliberate build choice, not a default replacement for source loading: it trades some flexibility and build simplicity for reusable class files and deployment-time advantages. section 17.4

How Clojure: The Essential Reference covers it

How deep it goes
Explains class-file output, loading precedence, build trade-offs, and compile-time configuration; does not provide an incremental-build procedure.
Examples
Demonstrates compiled namespaces, a generated Java `main` method, and builds that omit assertions or colocated tests.
What it assumes
Assumes familiarity with Clojure namespaces and basic Java interoperability while demonstrating `compile` and generated class files.
What you can do after
Operate/configure it with `compile` and compile-time bindings, while weighing startup and interoperability benefits against version and build constraints.
How it connects
Connects `gen-class`, `deftype`, Java interoperability, and compile-time dynamic variables.

Also explained in

Compare how 3 books explain ahead-of-time compilation →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Clojure: The Essential Reference. The section links point to the parts of the book it draws on.