Agents, Vars and Dynamic Binding

25.1 Agents

(def log-agent (agent []))
(send log-agent conj "started")          ; asynchronous; runs on a fixed thread pool
(send-off log-agent (fn [log] (Thread/sleep 100) (conj log "io done")))   ; for blocking work
(await log-agent)                        ; wait for pending actions
@log-agent                               ; ["started" "io done"]

(def writer (agent nil))
(defn log-line! [msg] (send-off writer (fn [_] (spit "app.log" (str msg "\n") :append true))))

(agent-error log-agent)                  ; inspect a failed agent
(restart-agent log-agent [] :clear-actions true)
(set-error-handler! log-agent (fn [a ex] (println "agent error" ex)))
(shutdown-agents)                        ; stop the agent thread pools at program end

25.2 Vars and Dynamic Binding

Prefer passing configuration explicitly. Dynamic vars are best for cross-cutting context (*out*, *print-length*, request context).

(def ^:dynamic *db-url* "jdbc:dev")
(defn query [] (str "querying " *db-url*))
(query)                                  ; "querying jdbc:dev"
(binding [*db-url* "jdbc:test"]          ; thread-local rebinding
  (query))                               ; "querying jdbc:test"

(alter-var-root #'*db-url* (constantly "jdbc:prod"))   ; change the root value
(with-redefs [query (fn [] "mocked")]    ; temporary redefinition (testing)
  (query))                               ; "mocked"

#'query                                  ; the var itself
(meta #'query)                           ; metadata: :ns :name :file :line :arglists :doc
((var query))                            ; calling through the var (sees redefinitions)