TIL from @andersmurphy: How to use macro to fail at compile time for resources and configuration keys. I wonder if I can fail at compile time also spec.
(defn resource->bytes [resource]
(-> resource io/input-stream InputStream/.readAllBytes))
(defmacro load-resource
"Fails at compile time if resource doesn't exists."
[path]
(let [res (io/resource path)]
(assert res (str path " not found."))
`(resource->bytes (io/resource ~path))))
(def env-data
(when-let [env-file (io/resource ".env.edn")]
(read-config env-file)))
(defmacro env
"Read env from .env.edn. If env is missing fails at compile time."
[k]
(if (k env-data)
;; We could just inline the value, but that makes it trickier
;; to patch env values on a running server from the REPL.
`(env-data ~k)
(throw (ex-info (str "Missing env in .env.edn: " k)
{:missing-env k}))))Sure, also via a macro. Although technically it's not "compile time" but "macro expansion time". In Clojure, compilation and evaluation go hand in hand (unless things have been AOT-compiled), and compilation includes macro expansion. You cannot compile without expanding macros, but you can expand macros without compilation.
@p-himik good point.
Glad you found it helpful ❤️ . To @p-himik point it is technically more nuanced than compile time. You can even use macros with eval at runtime to inline cache functions at runtime. But, I felt the simplification (although technically incorrect) made the article more understandable at the time.