(compile 'my.namespace) ;; writes .class files to *compile-path*
Did you know:
Most libraries are distributed as source and you compile them over and over
You can compile namespaces explicitly using compile
Namespace compilation is transitive
compile
writes these files to disk and require
will use them
You can use compile
on the namespace you load when you start development, or on your user.clj
, or on the main namespace you run as a server to improve your startup time
The compile
function takes a namespace symbol and compiles that namespace and all the namespaces it requires into *compile-path*
(which defaults to classes
). That directory must exist and be on your classpath:
(compile 'my.namespace) ;; writes .class files to *compile-path*
Subsequently, when any of those compiled namespaces are required, the class file will be loaded, rather than the original .clj
file. If a source file is updated (and thus newer), it will be loaded instead. Periodically, you will need to re-compile to account for new dependencies or changing code.
Note that compilation is a side effect of loading, so has no effect on already loaded namespaces. In particular, one special case is the user.clj
file, which is loaded automatically by the Clojure runtime. If you are using a user.clj
in dev (or need to compile namespaces that are already compiled), you can do so by forcing a reload while compiling:
(binding [*compile-files* true] ;; compile during load
(require 'user :reload-all)) ;; reload this and all transitively loaded namespaces
That’s it! This technique can substantially reduce your startup time during development, particularly as the number of dependencies you load increases.
In a deps.edn project, you should create a development alias (like :dev
) that includes the classes
directory:
{:deps { ... }
:aliases
{:dev {:extra-paths ["classes"]}}}
You will also need to ensure the classes
directory exists. It may be useful to include the empty classes
directory as part of your version controlled project structure (make sure to ignore and NOT include the compiled .class files).
You can then start your REPL with the alias and compile
to populate your classes
directory and start seeing the benefits:
$ clj -A:dev
Clojure 1.10.1
user=> (compile 'my.namespace)
If you want to use an automatically loaded user.clj, you should incorporate that into your dev alias by adding a source path dev
:
{:deps { ... }
:aliases
{:dev {:extra-paths ["dev" "classes"]}}}
And then create dev/user.clj
:
(ns user
(:require ... ))
;; dev-only functions, etc
Remember to use the modified compilation process for user.clj:
$ clj -A:dev
Clojure 1.10.1
user=> (binding [*compile-files* true] (require 'user :reload-all))
Original author: Alex Miller