什么时候通过nREPL重新加载命名空间还不够,整个服务器进程(甚至REPL)需要重新启动



在Emacs的nREPL和Cider模式下玩了一点之后,我发现有时我必须重新启动我的http工具包服务器,让它接收更改(通常是在更改中间件或路由时),或者事情变得一团糟,我只需要重新启动整个REPL。

我很难弄清楚到底是什么原因造成的。似乎有时我甚至可以更改路由定义(用compjuredefroutes宏定义),运行中的服务器会处理问题,有时它不起作用,我不得不停止服务器并重新启动它

Clojure中是否有任何特定的模式可以使重新加载更容易?或者是不可能强制重新加载整个REPL的事情?我还发现,有时我会评估一个只会把事情搞砸的东西,比如一个不合格的导入,这会迫使我重新启动整个REPL。

是否有任何最佳实践可以在服务器运行时轻松地重新加载代码(例如通过Cider中的C-c C-k),以便自动获取更改?我想我很久以前就在某个地方看到过,即使是最愚蠢的web服务器也可以通过添加一层间接层来重新加载东西,但我似乎真的找不到它在哪里。

根据我的经验,REPL中最常见的两个过时代码源是高阶函数和AOT编译。

高阶函数

当您使用诸如comppartial之类的高阶函数,该函数以实现某些应用程序逻辑的函数为输入并返回调用它的新函数时,会捕获调用HOF时函数的定义。输入函数定义的后续更改将不会反映在输出函数中。

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

在上面的示例中,如果a在命名空间foo中定义,而b在命名空间bar中定义,那么重新加载foo将不会改变b的行为,除非您也重新加载了bar。如示例所示,如果定义发生更改,则在调用代码中按名称调用函数将导致调用新定义。

这与web开发相关,因为Clojure web开发的许多基础设施都依赖于高阶功能(即中间件本质上是功能组合)。您应该根据自己的判断,在高阶函数的惯用用法和开发过程中重新加载代码的方便性之间做出适当的平衡。

AOT编译

一些Clojure特性(即deftypedefrecorddefprotocol)导致生成Java类和接口,然后在Clojure代码的其他地方用它们的Java类名引用这些类和接口。当AOT编译此代码时,会为这些Java类发出.class文件,这些文件随后出现在REPL类路径中。加载Java类时,总是优先考虑.class文件中的定义,而不是动态生成的类定义。通过这些机制之一重新加载定义Java类的名称空间不会有任何效果;必须重新启动REPL。

最新更新