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

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

  • 编辑src/foo/bar.clj
  • 关闭REPL
  • 打开REPL
  • (load-file "src/foo/bar.clj")
  • (use 'foo.bar)
  • 另外, (use 'foo.bar :reload-all)不会产生所需的效果,它会评估修改过的函数体并返回新的值,而不是表现为源完全没有改变。


    或者(use 'your.namespace :reload)


    还有一种方法就像使用tools.namespace一样,它非常高效:

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

    使用(require … :reload)重新加载Clojure代码和:reload-all是非常有问题的:

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

  • 如果您从源文件中删除定义并重新加载它们,则这些定义在内存中仍可用。 如果其他代码依赖于这些定义,它将继续工作,但在下次重新启动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帮助器函数(如docsource )不再被引用。

    为了解决这些问题,我更愿意为用户命名空间创建一个实际的源文件,以便它可以可靠地重新加载。 我把源文件放在~/.lein/src/user.clj但你可以放在任何地方。 该文件应该要求顶层ns声明中的刷新函数如下所示:

    (ns user
      (:require [clojure.tools.namespace.repl :refer [refresh]]))
    

    您可以在~/.lein/profiles.clj设置leiningen用户配置~/.lein/profiles.clj以便将文件放入的位置添加到类路径中。 配置文件应该看起来像这样:

    {:user {:dependencies [[org.clojure/tools.namespace “0.2.7”]]
            :repl-options { :init-ns user }
            :source-paths [“/Users/me/.lein/src”]}}
    

    请注意,我在启动REPL时将用户名称空间设置为入口点。 这确保REPL帮助器函数在用户名称空间中被引用,而不是应用程序的主名称空间中。 这样他们不会迷路,除非你改变我们刚刚创建的源文件。

    希望这可以帮助!

    链接地址: http://www.djcxy.com/p/38579.html

    上一篇: How to reload a clojure file in REPL

    下一篇: Using ETAGS/CTAGS in emacs with C++ Project