如何在 REPL 中重新加载 clojure 文件
无需重新启动 REPL 即可重新加载 Clojure 文件中定义的函数的首选方法是什么?现在,为了使用更新的文件,我必须:
- 编辑
src/foo/bar.clj
- 关闭 REPL
- 打开 REPL
(load-file "src/foo/bar.clj ")
(use 'foo.bar)
另外,(use 'foo.bar :reload-all)
并没有达到所需要的效果,即评估修改后的函数体并返回新值,而不是表现为源根本没有改变。
文档:
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:
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
或者
(使用'your.namespace:reload)
Or
(use 'your.namespace :reload)
还有一种替代方法,例如使用 tools.namespace,它非常有效:
There is also an alternative like using tools.namespace, it's pretty efficient:
使用
(require … :reload)
和:reload-all
重新加载 Clojure 代码是 非常有问题:改善了这种情况。它提供了一个简单的刷新功能,可以根据命名空间的依赖关系图进行智能重新加载。
不幸的是,如果命名空间的依赖关系图,则第二次重新加载将失败您在其中引用的
refresh
函数发生了变化,这是因为 tools.namespace 在加载新代码之前破坏了命名空间的当前版本,您可以使用完全限定的 var 名称作为解决方法。为了这问题,但我个人更喜欢不必在每次刷新时都输入该内容。上述的另一个问题是,在重新加载主命名空间后,标准 REPL 辅助函数(如 doc 和 source ) >) 不再在那里引用。
为了解决这些问题,我更喜欢为用户命名空间创建一个实际的源文件,以便可以可靠地重新加载该源文件。 clj 但你可以放入任何地方。该文件应该需要顶部 ns 声明中的刷新功能,如下所示:
您可以设置
~/.lein/profiles.clj
中的 leiningen 用户配置文件,以便将文件所在的位置添加到类路径中。配置文件应如下所示:请注意,我在启动 REPL 时将用户命名空间设置为入口点。这确保了 REPL 辅助函数在用户命名空间而不是应用程序的主命名空间中被引用。这样,除非您更改我们刚刚创建的源文件,否则它们就不会丢失。
希望这有帮助!
Reloading Clojure code using
(require … :reload)
and:reload-all
is very problematic: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.
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.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
andsource
) 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: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: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!
最好的答案是:
这不仅会重新加载您指定的命名空间,还会重新加载所有依赖项命名空间。
文档:
需要
The best answer is:
This will not only reload your specified namespace, but will reload all dependency namespaces as well.
Documentation:
require
基于帕帕坎的回答的一条内线:
One liner based on papachan's answer:
我在 Lighttable(以及很棒的 instarepl)中使用它,但它应该在其他开发工具中使用。我遇到了同样的问题,旧的函数定义和多方法在重新加载后仍然存在,所以现在在开发过程中而不是声明命名空间:
我像这样声明我的命名空间:
相当丑陋,但每当我重新评估整个命名空间时(Cmd-Shift-在Lighttable中输入以获得每个表达式的新instarepl结果),它吹走了所有旧的定义并给了我一个干净的环境。在我开始这样做之前,每隔几天我就会被旧的定义绊倒,它拯救了我的理智。 :)
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:
I declare my namespaces like this:
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. :)
再次尝试加载文件吗?
如果您使用 IDE,通常有一个键盘快捷键可以将代码块发送到 REPL,从而有效地重新定义相关函数。
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.
一旦
(use 'foo.bar)
为你工作,这意味着你的 CLASSPATH 上有 foo/bar.clj 或 foo/bar_init.class 。 bar_init.class 将是 bar.clj 的 AOT 编译版本。如果你这样做(use 'foo.bar)
,我不确定 Clojure 是否更喜欢 class 而不是 clj 或者相反。如果它更喜欢类文件并且您拥有这两个文件,那么很明显,编辑 clj 文件然后重新加载命名空间没有任何效果。顺便说一句:如果你的 CLASSPATH 设置正确,你不需要在
use
之前load-file
。BTW2:如果您出于某种原因需要使用
load-file
,那么您可以在编辑文件后再次执行此操作。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 theuse
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.