Clojure - 如何在运行时而不是编译时评估def表单

Leo*_*sov 9 clojure

我试图解决的核心问题是:每次启动程序时都有一个settings从a加载的对象settings.json file.

我最初使用的代码就像

(def settings (load-settings "settings.json"))

但在部署期间,我惊讶地发现init表单正在编译时而不是运行时进行评估 - 编译失败,因为没有settings.json文件到位.

所以问题的Y部分是 - 我可以延迟初始化形式的评估而不使用引用或以其他方式使对象的使用复杂化吗?或者我在这里错过了一些核心概念?

Leo*_*sov 10

回答我自己的问题,因为我碰巧在@Alex找到了我想要的确切内容.

所以,从2008年的Clojure邮件列表中引用这个帖子,

编译时,会设置编译文件标志.[...]如果你的文件有一些def初始化器,你不想在编译时运行,你可以像这样条件化它们:

(def foo (when-not *compile-files* (my-runtime-init)))

在这种情况下,foo在编译时将为零.

我使用以下代码测试了该行为:

(def evaluated-at-runtime
  (when-not *compile-files*
    (println "i am evaluated")
    "value"))

(defn -main [& args]
  (println evaluated-at-runtime))


>lein uberjar
... "(i am evaluated)" is not printed)

>java -jar test-app.jar
i am evaluated
value
Run Code Online (Sandbox Code Playgroud)

这里有一个警告,一旦程序启动,在-main方法之前就会对init表单进行评估,这可能对每个人都不够灵活,但是我会引导你对这个问题做出其他一个很好的回答.


Leo*_*tny 8

一种可能的解决方案是懒惰地加载您的设置文件,即第一次实际使用它.你可以用delay来做:

(def settings
  (delay (load-settings "settings.json")))
Run Code Online (Sandbox Code Playgroud)

唯一的区别是,每次你想要使用它时你都必须对你的对象进行derefsettings:

(println @settings)
Run Code Online (Sandbox Code Playgroud)