How to reload a clojure file in REPL

ClojureReloadRead Eval-Print-LoopLeiningen

Clojure Problem Overview


What is the preferred way of reloading functions defined in a Clojure file without having to restart the REPL. Right now, in order to use the updated file I have to:

  • edit src/foo/bar.clj
  • close the REPL
  • open the REPL
  • (load-file "src/foo/bar.clj")
  • (use 'foo.bar)

In addition, (use 'foo.bar :reload-all) does not result in required effect, which is evaluating the modified bodies of functions and returning new values, instead of behaving as the source haven't changed at all.

Documentation:

Clojure Solutions


Solution 1 - Clojure

Or (use 'your.namespace :reload)

Solution 2 - Clojure

There is also an alternative like using tools.namespace, it's pretty efficient:

user=> (use '[clojure.tools.namespace.repl :only (refresh)])

user=> (refresh)

:reloading (namespace.app)

:ok

Solution 3 - Clojure

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!

Solution 4 - Clojure

The best answer is:

(require 'my.namespace :reload-all)

This will not only reload your specified namespace, but will reload all dependency namespaces as well.

Documentation:

require

Solution 5 - Clojure

One liner based on papachan's answer:

(clojure.tools.namespace.repl/refresh)

Solution 6 - Clojure

I use this in Lighttable (and the awesome instarepl) but it should be of use in other development tools. I was having the same problem with old definitions of functions and multimethods hanging around after reloads so now during development instead of declaring namespaces with:

(ns my.namespace)

I declare my namespaces like this:

(clojure.core/let [s 'my.namespace]
                  (clojure.core/remove-ns s)
                  (clojure.core/in-ns s)
                  (clojure.core/require '[clojure.core])
                  (clojure.core/refer 'clojure.core))

Pretty ugly but whenever I re-evaluate the entire namespace (Cmd-Shift-Enter in Lighttable to get the new instarepl results of each expression), it blows away all old definitions and gives me a clean environment. I was tripped up every few days by old definitions before I started doing this and it has saved my sanity. :)

Solution 7 - Clojure

Try load-file again?

If youre using an IDE, there's usually a keyboard shortcut to send a code-block to the REPL, thus effectively re-defining the associated functions.

Solution 8 - Clojure

As soon as (use 'foo.bar) works for you, it means that you have foo/bar.clj or foo/bar_init.class on your CLASSPATH. The bar_init.class would be an AOT-compiled version of bar.clj. If you do (use 'foo.bar), I'm not exactly sure if Clojure prefers class over clj or the other way round. If it would prefer class files and you have both files, then it's clear that editing the clj file and then reloading the namespace has no effect.

BTW: You don't need to load-file before the use if your CLASSPATH is set properly.

BTW2: If you need to use load-file for a reason, then you can simply do it again if you edited the file.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionpkaletaView Question on Stackoverflow
Solution 1 - ClojureMingView Answer on Stackoverflow
Solution 2 - ClojurepapachanView Answer on Stackoverflow
Solution 3 - ClojureDirk GeursView Answer on Stackoverflow
Solution 4 - ClojureAlan ThompsonView Answer on Stackoverflow
Solution 5 - ClojureJiezhen YiView Answer on Stackoverflow
Solution 6 - ClojureoptevoView Answer on Stackoverflow
Solution 7 - ClojurePaul LamView Answer on Stackoverflow
Solution 8 - ClojureTassilo HornView Answer on Stackoverflow