Skip to content

Instantly share code, notes, and snippets.

@shaunlebron
Last active November 5, 2025 21:31
Show Gist options
  • Save shaunlebron/0950a2a2e65beec6c121dff3de6309f1 to your computer and use it in GitHub Desktop.
Save shaunlebron/0950a2a2e65beec6c121dff3de6309f1 to your computer and use it in GitHub Desktop.
Safe Navigation in Clojure

Safe Navigation in Clojure

safe-> is a safe navigation macro for Clojure. It extends some-> with additional guards:

  1. doesn’t call nil values
  2. doesn’t call missing methods (ClojureScript only)

For example, call foo if not nil:

JS foo?.()
Clojure (when foo (foo))
Clojure macro (safe-> (foo))

For chaining:

JS foo?.bar?.baz?.()
ClojureScript (some-> foo .-bar .baz) ← ERROR if baz is nil
ClojureScript macro (safe-> foo .-bar .baz) ← SAFE if baz is nil
(defn safe-call* [call]
(or (when (seq? call)
(let [[a b] call]
(when (symbol? a)
(condp re-find (name a)
;; Don’t call missing methods (ClojureScript only)
#"^\.[a-zA-Z]" `(when (~(symbol (clojure.string/replace (name a) #"^." ".-")) ~b) ~call)
;; Don’t call nil
#"^[a-zA-Z]" `(when ~a ~call)
nil))))
call))
(defmacro safe->
"same as `some->` but protects against calling nil values"
[expr & forms]
(let [g (gensym)
expr (safe-call* expr)
steps (map (fn [step] `(if (nil? ~g)
nil
~(safe-call*
(if (seq? step)
(list* (first step) g (rest step))
(list step g)))))
forms)]
`(let [~g ~expr
~@(interleave (repeat g) (butlast steps))]
~(if (empty? steps)
g
(last steps)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment