Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在REPL中重新加载clojure文件_Clojure_Reload_Read Eval Print Loop_Leiningen - Fatal编程技术网

如何在REPL中重新加载clojure文件

如何在REPL中重新加载clojure文件,clojure,reload,read-eval-print-loop,leiningen,Clojure,Reload,Read Eval Print Loop,Leiningen,在不必重新启动REPL的情况下,重新加载Clojure文件中定义的函数的首选方法是什么。现在,为了使用更新的文件,我必须: 编辑src/foo/bar.clj 关闭REPL 打开REPL (加载文件“src/foo/bar.clj”) (使用“foo.bar) 此外,(使用'foo.bar:reload all)不会产生所需的效果,即评估修改后的函数体并返回新值,而不是表现为源代码根本没有更改 文档: 是否再次尝试加载文件 如果您使用的是IDE,通常会有一个键盘快捷键向REPL发送代码

在不必重新启动REPL的情况下,重新加载Clojure文件中定义的函数的首选方法是什么。现在,为了使用更新的文件,我必须:

  • 编辑
    src/foo/bar.clj
  • 关闭REPL
  • 打开REPL
  • (加载文件“src/foo/bar.clj”)
  • (使用“foo.bar)
此外,
(使用'foo.bar:reload all)
不会产生所需的效果,即评估修改后的函数体并返回新值,而不是表现为源代码根本没有更改

文档:

是否再次尝试加载文件

如果您使用的是IDE,通常会有一个键盘快捷键向REPL发送代码块,从而有效地重新定义相关函数。

只要
(使用'foo.bar)
对您有效,就意味着您的类路径上有foo/bar.clj或foo/bar\u init.class。bar_init.class将是bar.clj的AOT编译版本。如果您使用
(使用'foo.bar)
,我不确定Clojure是更喜欢类而不是clj,还是相反。如果它更喜欢类文件,而您同时拥有这两个文件,那么很明显,编辑clj文件然后重新加载名称空间没有任何效果

顺便说一句:如果类路径设置正确,则在使用
之前不需要
加载文件


BTW2:如果您出于某种原因需要使用
加载文件
,那么如果您编辑了文件,您只需再次使用即可。

还有一种类似使用的替代方法,它非常有效:

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

user=> (refresh)

:reloading (namespace.app)

:ok
(使用'your.namespace:reload)
最佳答案是:

(require 'my.namespace :reload-all)
这不仅会重新加载指定的命名空间,还会重新加载所有依赖项命名空间

文档:


使用
(要求…:重新加载)
:重新加载全部
重新加载Clojure代码是:

  • 如果修改两个相互依赖的名称空间,则必须 记住以正确的顺序重新加载它们以避免编译 错误

  • 如果从源文件中删除定义,然后重新加载它, 这些定义在内存中仍然可用。如果其他代码 根据这些定义,它将继续工作,但会 下次重新启动JVM时中断

  • 如果重新加载的命名空间包含
    defmulti
    ,则还必须重新加载 所有关联的
    defmethod
    表达式

  • 如果重新加载的命名空间包含
    defprotocol
    ,则还必须 重新加载实现该协议的任何记录或类型并替换 具有新实例的这些记录/类型的任何现有实例

  • 如果重新加载的命名空间包含宏,则还必须重新加载任何宏 使用这些宏的名称空间

  • 如果正在运行的程序包含关闭中的值的函数 在重新加载的命名空间中,那些关闭的值不会更新。 (这在构造“处理程序”的web应用程序中很常见 堆栈”作为函数的组合。)

clojure.tools.namespace库显著改善了这种情况。它提供了一个简单的刷新函数,可以根据名称空间的依赖关系图进行智能重新加载

myapp.web=> (require '[clojure.tools.namespace.repl :refer [refresh]])
nil
myapp.web=> (refresh)
:reloading (myapp.web)
:ok
不幸的是,如果引用
refresh
函数的命名空间发生更改,则第二次重新加载将失败。这是因为tools.namespace在加载新代码之前会销毁命名空间的当前版本

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)
您可以使用完全限定的var名称作为解决此问题的方法,但就我个人而言,我不希望在每次刷新时都键入该名称。上面提到的另一个问题是,在重新加载主名称空间后,标准的REPL helper函数(如
doc
source
)不再在那里引用

为了解决这些问题,我更愿意为用户名称空间创建一个实际的源文件,以便可以可靠地重新加载它。我把源文件放在
~/.lein/src/user.clj
中,但您可以放在任何地方。该文件需要在top ns声明中使用以下刷新功能:

(ns user
  (:require [clojure.tools.namespace.repl :refer [refresh]]))
{:user {:dependencies [[org.clojure/tools.namespace "0.2.7"]]
        :repl-options { :init-ns user }
        :source-paths ["/Users/me/.lein/src"]}}
(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))
您可以在
~/.lein/profiles.clj
中进行设置,以便将文件放置的位置添加到类路径中。配置文件应如下所示:

(ns user
  (:require [clojure.tools.namespace.repl :refer [refresh]]))
{:user {:dependencies [[org.clojure/tools.namespace "0.2.7"]]
        :repl-options { :init-ns user }
        :source-paths ["/Users/me/.lein/src"]}}
(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))
注意,我在启动REPL时将用户名称空间设置为入口点。这可以确保在应用程序的用户名称空间而不是主名称空间中引用REPL helper函数。这样它们就不会丢失,除非您更改我们刚刚创建的源文件


希望这有帮助

一行基于papachan的回答:

(clojure.tools.namespace.repl/refresh)
我在Lighttable(和令人敬畏的instarepl)中使用了它,但它应该在其他开发工具中使用。在重新加载后,我对旧的函数和多方法定义也有同样的问题,所以现在在开发过程中,我没有使用以下方法声明名称空间:

(ns my.namespace)
我这样声明我的名称空间:

(ns user
  (:require [clojure.tools.namespace.repl :refer [refresh]]))
{:user {:dependencies [[org.clojure/tools.namespace "0.2.7"]]
        :repl-options { :init-ns user }
        :source-paths ["/Users/me/.lein/src"]}}
(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))

非常难看,但每当我重新计算整个名称空间(在Lighttable中输入Cmd Shift以获得每个表达式的新instarepl结果)时,它就会清除所有旧定义,为我提供一个干净的环境。在我开始做这件事之前,每隔几天我就会被旧的定义绊倒,这让我保持了理智

(使用'foo.bar:reload all)
对我来说一直都很好。另外,如果您的类路径设置正确,
(加载文件)
应该是不必要的。什么是你没有得到的“必需效果”?是的,“必需效果”是什么?发布一个示例
bar.clj
详细说明“必需的效果”。所谓必需的效果,我的意思是,如果我有一个函数
(defn f[]1)
,并且我将其定义更改为
(defn f[]2)
,那么在我发布
之后