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

时间:2015-07-09 17:36:33

标签: clojure

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

我最初使用的代码如

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

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

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

4 个答案:

答案 0 :(得分:9)

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

所以,引用this thread from the Clojure mailing list from 2008

  

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

     

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

     

在这种情况下,foo在编译时将为nil。

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

(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

这里有一个警告,在-main方法之前,一旦程序启动就会对进行初始化评估,这可能对每个人来说都不够灵活,但后来我引导你回答这个问题的其他一个很好的回答。

答案 1 :(得分:8)

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

执行此操作
(def settings
  (delay (load-settings "settings.json")))

唯一的区别是,每当您想要使用它时,您必须deref settings个对象:

(println @settings)

答案 2 :(得分:0)

未来延迟都可以使用。

future在后台运行,避免等待评估完成。

delay不会尝试访问该值。

(def string-sample "(do (Thread/sleep 5000)  (println \"done\") 1)")

; execute immediately
(def settings (load-string string-sample))
(println settings)

; execute in the background, returns immediately
(def settings (future (load-string string-sample )))
(println @settings)

; evaluated when calling it
(def settings (delay (load-string string-sample)))
(println @settings)

答案 3 :(得分:0)

什么不只是使用defn而不是def。

(defn settings [] (load-settings "settings.json"))

然后使用(settings)代替settings