beginners

amiorin 2025-12-05T09:31:18.521699Z

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

p-himik 2025-12-05T09:43:42.286849Z

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.

amiorin 2025-12-05T09:57:48.052559Z

@p-himik good point.

2025-12-05T10:46:55.226749Z

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.