Reloading Clojure code using (require … :reload)
and :reload-all
is very problematic:
If you modify two namespaces which depend on each other, you must
remember to reload them in the correct order to avoid compilation
errors.
If you remove definitions from a source file and then reload it,
those definitions are still available in memory. If other code
depends on those definitions, it will continue to work but will
break the next time you restart the JVM.
If the reloaded namespace contains defmulti
, you must also reload
all of the associated defmethod
expressions.
If the reloaded namespace contains defprotocol
, you must also
reload any records or types implementing that protocol and replace
any existing instances of those records/types with new instances.
If the reloaded namespace contains macros, you must also reload any
namespaces which use those macros.
If the running program contains functions which close over values in
the reloaded namespace, those closed-over values are not updated.
(This is common in web applications which construct the "handler
stack" as a composition of functions.)
The clojure.tools.namespace library improves the situation significantly. It provides an easy refresh function that does smart reloading based on a dependency graph of the namespaces.
myapp.web=> (require '[clojure.tools.namespace.repl :refer [refresh]])
nil
myapp.web=> (refresh)
:reloading (myapp.web)
:ok
Unfortunately reloading a second time will fail if the namespace in which you referenced the refresh
function changed. This is due to the fact that tools.namespace destroys the current version of the namespace before loading the new code.
myapp.web=> (refresh)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: refresh in this context, compiling:(/private/var/folders/ks/d6qbfg2s6l1bcg6ws_6bq4600000gn/T/form-init819543191440017519.clj:1:1)
You could use the fully qualified var name as a workaround for this problem but personally I prefer not having to type that out on each refresh. Another problem with the above is that after reloading the main namespace the standard REPL helper functions (like doc
and source
) are no longer referenced there.
To solve these issues I prefer to create an actual source file for the user namespace so that it can be reliably reloaded. I put the source file in ~/.lein/src/user.clj
but you can place in anywhere. The file should require the refresh function in the top ns declaration like this:
(ns user
(:require [clojure.tools.namespace.repl :refer [refresh]]))
You can setup a leiningen user profile in ~/.lein/profiles.clj
so that location you put the file in is added to the class path. The profile should look something like this:
{:user {:dependencies [[org.clojure/tools.namespace "0.2.7"]]
:repl-options { :init-ns user }
:source-paths ["/Users/me/.lein/src"]}}
Note that I set the user namespace as the entry point when launching the REPL. This ensures that the REPL helper functions get referenced in the user namespace instead of the main namespace of your application. That way they won’t get lost unless you alter the source file we just created.
Hope this helps!
(use 'foo.bar :reload-all)
has always worked fine for me. Also,(load-file)
should never be necessary if you have your classpath set up right. What is the "required effect" you're not getting? – Thrallbar.clj
detailing on the "required effect". – Pinole(defn f [] 1)
and I changed its definition to(defn f [] 2)
, it seemed to me that after I issue(use 'foo.bar :reload-all)
and call thef
function it should return 2, not 1. Unfortunately it doesn't work that way for me and every time I change the body of function I have to restart the REPL. – Barnett:reload
or:reload-all
should both work. – Weak