简体   繁体   中英

When is reloading a namespace via nREPL not enough and whole server process (or even the REPL) needs to be restarted?

Having played around a little bit with nREPL and Cider mode for Emacs, I've found that sometimes I have to either re-start my http-kit server for it to pickup the changes (usually when changing middleware or routes), or things get messy and I just have to restart the whole REPL.

I'm having hard trouble finding out what actually causes this. It seems that sometimes I can change even things like route definitions (defined with the compojure defroutes macro), and the running server will pick things up, and sometimes it doesn't work, and I have to stop the server and restart it.

Are there any specific patterns in Clojure that make reloading easier? Or things that make it impossible forcing a whole REPL reload? I've also found that sometimes I eval a something that just messes things up, such as an unqualified import, and that forces me to restart the whole REPL.

Are there any best practices that make code easily reloadable (such as via Cc Ck in Cider) while a server is running, so that the changes are picked up automatically ? I think I've seen something somewhere a long time ago, that it is possible to make even the dumbest web server reload things by adding a layer of indirection, but I can't really seem to find where that was.

In my experience, the two most common sources of stale code in the REPL are higher-order functions and AOT compilation.

Higher-Order Functions

When you use a higher-order function such as comp or partial that takes a function as input that implements some application logic and returns a new function that invokes it, the definition of the function at the time the HOF was called is captured. Subsequent changes to the definition of the input function will not be reflected in the output function.

user=> (defn a [x y] (+ x y))
user=> (def b (partial a 2))
user=> (defn c [y] (a 2 y))
user=> (b 3)
5
user=> (c 3)
5
user=> (defn a [x y] (* x y))
user=> (b 3)
5
user=> (c 3)
6

In the above example, if a is defined in namespace foo and b is defined in namespace bar , then reloading foo will not alter the behavior of b unless you also reload bar . As suggested in the example, invoking the function by name in calling code will cause the new definition to be invoked if the definition changes.

This is relevant for web development because lots of the infrastructure for Clojure web development is heavy on higher-order functions (ie middleware is essentially function composition). You should use your own judgement to decide on an appropriate balance of idiomatic usage of higher-order functions and ease of reloading code during development.

AOT Compilation

Some Clojure features (namely deftype , defrecord , and defprotocol ) cause Java classes and interfaces to be generated, which are then referred to by their Java classname elsewhere in Clojure code. When you AOT-compile this code, .class files are emitted for these Java classes which are then present on the REPL classpath. When loading a Java class, preference is always given to definitions in .class files over dynamically generated class definitions. Reloading a namespace that defines a Java class via one of these mechanisms will not have any effect; you must restart the REPL.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM