How to reload a clojure file in REPL

73,553

Solution 1

Or (use 'your.namespace :reload)

Solution 2

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

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

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

One liner based on papachan's answer:

(clojure.tools.namespace.repl/refresh)
Share:
73,553

Related videos on Youtube

pkaleta
Author by

pkaleta

Updated on December 04, 2020

Comments

  • pkaleta
    pkaleta over 3 years

    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:

    • Dave Ray
      Dave Ray over 12 years
      (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?
    • Sridhar Ratnakumar
      Sridhar Ratnakumar over 12 years
      Yes, what is the "required effect"? Post a sample bar.clj detailing on the "required effect".
    • pkaleta
      pkaleta over 12 years
      By required effect I meant that if I had a function (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 the f 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.
    • Jason
      Jason about 8 years
      You must have another problem in your setup... :reload or :reload-all should both work.
  • AnnanFay
    AnnanFay almost 11 years
    Not sure why this is marked as the correct answer. It doesn't answer the question clearly.
  • ctford
    ctford over 10 years
    As someone coming to this question, I don't find this answer very clear.
  • Bahadir Cambel
    Bahadir Cambel over 9 years
    this answer is more proper
  • Dave Yarwood
    Dave Yarwood over 9 years
    Caveat: running (refresh) seems to also cause the REPL to forget that you've required clojure.tools.namespace.repl. Subsequent calls to (refresh) will give you a RuntimeException, "Unable to resolve symbol: refresh in this context." Probably the best thing to do is to either (require 'your.namespace :reload-all), or, if you know you're going to want to refresh your REPL a lot for a given project, make a :dev profile and add [clojure.tools.namespace.repl :refer (refresh refresh-all)] to dev/user.clj.
  • Alan Thompson
    Alan Thompson about 9 years
    Good suggestions. One question: why the ":source-paths" entry above?
  • Alan Thompson
    Alan Thompson about 9 years
    OK, found the answer. The file "user.clj" needs to live somewhere, and a good place is "/home/alan/.lein/user.clj" (on linux). For lein to find the file, we need a file "/home/alan/.lein/profiles.clj " with an entry like: ':source-paths [ "/home/alan/.lein" '
  • Dirk Geurs
    Dirk Geurs about 9 years
    Exactly, having an actual source file makes it possible to reliably reload that file and the namespace within. Here is the file I'm currently using: github.com/Dirklectisch/.lein/blob/master/src/user.clj
  • Dirk Geurs
    Dirk Geurs about 9 years
    @fl00r Sorry not quite getting your point. It should be :source-paths in this case since we are loading source files not other resources.
  • fl00r
    fl00r about 9 years
    @DirkGeurs, with :source-paths I get #<FileNotFoundException java.io.FileNotFoundException: Could not locate user__init.class or user.clj on classpath: >, while with :resource-paths everything is ok.
  • Dirk Geurs
    Dirk Geurs about 9 years
    @fl00r Have you created the source file user.clj? "To solve these issues I prefer to create an actual source file for the user namespace so that it can be reliably reloaded."
  • fl00r
    fl00r about 9 years
    Yes I did. Now it is stored in ~/.lein/user/user.clj with other stuff
  • Dirk Geurs
    Dirk Geurs about 9 years
    @fl00r and it still throws that error? Do you have a valid project.clj in the folder you are launching the REPL from? That might fix your problem.
  • fl00r
    fl00r about 9 years
    Yes, it is pretty standard, and all works fine with :resource-paths, I am in my user namespace inside repl.
  • Aaron Digulla
    Aaron Digulla almost 9 years
    This is the only answer which worked with lein repl, Coljure 1.7.0 and nREPL 0.3.5. If you're new to clojure: The namespace ('my.namespace) is defined with (ns ...) in src/.../core.clj, for example.
  • Jason
    Jason about 8 years
    :reload-all should also work. The OP specifically says it doesn't, but I think there was something else wrong in the OP's dev environment because for a single file the two (:reload and :reload-all) should have the same effect. Here's the full command for :reload-all: (use 'your.namespace :reload-all) This reloads all the dependencies, too.
  • matanster
    matanster almost 7 years
    if that's the case, then the clojure repl really sucks.
  • jgomo3
    jgomo3 almost 6 years
    The problem with this answer is that the original question is using (load-file ...), no require. How can her add the :reload-all to the namespace after the load-file?
  • Alan Thompson
    Alan Thompson almost 6 years
    Because the namespace structure like proj.stuff.core mirrors the file structure on disk like src/proj/stuff/core.clj, the REPL can locate the correct file and you don't need load-file.
  • David Tonhofer
    David Tonhofer almost 5 years
    Blogpost on the Clojure workflow by the author of tools.namespace: thinkrelevance.com/blog/2013/06/04/clojure-workflow-reloaded
  • Alper
    Alper over 4 years
    I'm doing a tutorial. Is it ok if I reload-all there?
  • Dirk Geurs
    Dirk Geurs over 4 years
    @Alper Yes you will probably be alright or at least you will notice soon enough if you hit one of the cases described above.
  • Alper
    Alper over 4 years
    I just had a great time working with a REPL that was lying to me because of this reload issue. Then it turned out everything I thought was working was not anymore. Maybe somebody should fix this situation?
  • Dirk Geurs
    Dirk Geurs over 4 years
    @Alper I don't think they will address this any time soon in the language. tools.namespace is you best bet. Cool to hear that you are trying out Clojure. There is a lot to love there.
  • Alper
    Alper over 4 years
    Hey! It seems like that and now that I got setup with Calva at a basic level things are looking up.
  • Dima Fomin
    Dima Fomin almost 4 years
    Can tools.namespace helps somehow to reimport recompiled Java classes as well?
  • Dima Fomin
    Dima Fomin almost 4 years
    @fl00r Seems :source-paths indeed cause an error when lein repl called in folder without project.clj (while :resource-paths does not)