The core problem I'm trying to solve is: have a settings
object that is loaded from a settings.json file
, every time when the program is started.
I initially used code like
(def settings (load-settings "settings.json"))
but during deploy I was surprised to discover that the init form is being evaluated at compile time rather than run time - the compilation was failing because there was no settings.json
file in place.
So the Y part of the problem is - can I delay the evaluation of the init form without employing refs or otherwise complicating the usage of the object? Or am I missing some core concept here?
Answering my own question because I happened to find the exact thing I wanted thanks to @Alex.
So, quoting this thread from the Clojure mailing list from 2008,
While compiling, a compile-files flag is set. [...] If your file has some def initializers you don't want to run at compile-time, you can conditionalize them like this:
(def foo (when-not *compile-files* (my-runtime-init)))
in which case foo will be nil at compile time.
I tested the behavior with the following code:
(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
There's a caveat here that the init form will be evaluated as soon as the program is launched, before the -main
method, this may not be flexible enough for everyone, but then I direct you to one of the other great responses to this question.
One possible solution is to load your settings file lazily, i.e. the first time it's actually used. You can do it with delay
macro:
(def settings
(delay (load-settings "settings.json")))
The only difference is that you'll have to deref your settings
object every time you'll want to use it:
(println @settings)
future and delay could both be used.
future runs in the background and avoid waiting for the evaluation to be finished.
delay would not do anything you try to access the value.
(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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With