Clojure-如何使def表单在运行时而不是编译时进行计算

Clojure-如何使def表单在运行时而不是编译时进行计算,clojure,Clojure,我试图解决的核心问题是:每次启动程序时,都要从settings.json文件加载一个settings对象 我最初使用的代码如下 (def设置(加载设置“settings.json”) 但是在部署期间,我惊讶地发现init表单是在编译时而不是在运行时评估的——编译失败了,因为没有settings.json文件 所以问题的Y部分是-我可以在不使用REF或使对象的使用复杂化的情况下延迟初始表单的计算吗?还是我在这里遗漏了一些核心概念 一种可能的解决方案是延迟加载设置文件,即第一次实际使用设置文件。您可

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

我最初使用的代码如下

(def设置(加载设置“settings.json”)

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


所以问题的Y部分是-我可以在不使用REF或使对象的使用复杂化的情况下延迟初始表单的计算吗?还是我在这里遗漏了一些核心概念

一种可能的解决方案是延迟加载设置文件,即第一次实际使用设置文件。您可以通过以下方式完成:

唯一的区别是,每次要使用
设置时,您都必须更改
对象:

(println @settings)
未来和延迟都可以使用

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

如果尝试访问该值,则不会执行任何操作

(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)

回答我自己的问题,因为多亏了@Alex,我碰巧找到了我想要的东西

所以引用,

编译时,会设置编译文件标志。[…]如果您的文件 有一些您不想在编译时运行的def初始值设定项,您可以 可以这样对它们进行条件化:

(def foo(非*编译文件*(我的运行时初始化))

在这种情况下,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
方法之前,程序一启动就会对init表单进行评估,这可能对每个人来说都不够灵活,但接下来我将向您介绍对这个问题的另一个很好的回答。

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

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

然后使用
(设置)
而不是
设置

是否编译名称空间?这是发生异常的时候吗?因为在许多用例中,您多次引用
设置
,有时每秒数百次,并且您不希望每次需要访问设置时都读取和解析文件。如果您想要缓存,您可以轻松地将其记忆。也可以缓存def表单中的值。或者,您可以添加一个参数,以允许函数使用者选择数据的缓存副本或新副本。
(defn settings [] (load-settings "settings.json"))