Lightweight Result monad for railway-oriented error handling.
ok results: {:ok value} err results: {:error category ...extra-data}
Lightweight Result monad for railway-oriented error handling.
ok results: {:ok value}
err results: {:error category ...extra-data}(bind result f)Monadic bind. If result is ok, applies f to the unwrapped value. If result is err, short-circuits and returns the error unchanged. f must return a Result.
Monadic bind. If result is ok, applies f to the unwrapped value. If result is err, short-circuits and returns the error unchanged. f must return a Result.
(ensure-result x)If x is already a Result (ok or err), return as-is. Otherwise wrap in ok. Used by ok-> and ok->> for smart-wrapping step results.
If x is already a Result (ok or err), return as-is. Otherwise wrap in ok. Used by ok-> and ok->> for smart-wrapping step results.
(err category)(err category data)Create an error Result with category keyword and optional data map.
Create an error Result with category keyword and optional data map.
(guard catch-class fallback & body)Selective catch — like rescue but for a specific Throwable subclass.
Like Erlang's try ... catch Class:Reason: structured error handling
where you care WHAT failed.
(guard Exception [] (risky-call)) ;; catch Exception only (guard java.io.IOException nil (slurp path)) ;; catch IOException only (guard AssertionError nil (validated-call x)) ;; catch :pre/:post failures
Error data shape: {::error {:message "..." :form "(risky-call)"}}
Selective catch — like rescue but for a specific Throwable subclass.
Like Erlang's `try ... catch Class:Reason`: structured error handling
where you care WHAT failed.
(guard Exception [] (risky-call)) ;; catch Exception only
(guard java.io.IOException nil (slurp path)) ;; catch IOException only
(guard AssertionError nil (validated-call x)) ;; catch :pre/:post failures
Error data shape: {::error {:message "..." :form "(risky-call)"}}(guard-fn catch-class f)(guard-fn catch-class f fallback)Like rescue-fn but catches a specific class. For selective pipelines.
(keep (guard-fn Exception #(parse %)) items) (map (guard-fn IOException #(read %) :missing) items)
Like rescue-fn but catches a specific class. For selective pipelines. (keep (guard-fn Exception #(parse %)) items) (map (guard-fn IOException #(read %) :missing) items)
(let-ok bindings & body)Monadic let for Results. Binds :ok values; short-circuits on first error.
(let-ok [x (may-fail) y (use x)] (ok (+ x y)))
Strict: every Result-bound RHS MUST evaluate to a Result map
({:ok ..} or {:error ..}). A non-Result value throws ex-info
with category :result/non-result-binding and the offending binding
symbol + form.
Pure-binding escape hatch: use :let [v expr ...] to bind plain
(non-Result) values inline, mirroring re-frame's let-fx and Clojure's
for/doseq :let support.
Monadic let for Results. Binds :ok values; short-circuits on first error.
(let-ok [x (may-fail)
y (use x)]
(ok (+ x y)))
Strict: every Result-bound RHS MUST evaluate to a Result map
(`{:ok ..}` or `{:error ..}`). A non-Result value throws `ex-info`
with category `:result/non-result-binding` and the offending binding
symbol + form.
Pure-binding escape hatch: use `:let [v expr ...]` to bind plain
(non-Result) values inline, mirroring re-frame's `let-fx` and Clojure's
`for`/`doseq` :let support.(map-err result f)Map over error values. Applies f to the error map (without :error key) and merges result back. Ok values pass through unchanged. f receives the full error result map.
Map over error values. Applies f to the error map (without :error key) and merges result back. Ok values pass through unchanged. f receives the full error result map.
(map-ok result f)Functor map over ok values. Applies f to the unwrapped value and re-wraps in ok. Errors pass through unchanged. Unlike bind, f returns a plain value (not a Result).
Functor map over ok values. Applies f to the unwrapped value and re-wraps in ok. Errors pass through unchanged. Unlike bind, f returns a plain value (not a Result).
(ok value)Wrap a value in a success Result.
Wrap a value in a success Result.
(ok-> expr & forms)Thread-first through Results with smart-wrap. Each step receives the unwrapped :ok value. If a step returns a Result, use it directly (bind). If it returns a plain value, auto-wrap in ok (fmap). Short-circuits on first error.
Like some-> but with Result-based error info instead of nil.
(ok-> (validate-order order catalog) ;; switch: may return err price-order ;; pure: auto-wrapped in ok (acknowledge create-letter) ;; switch: may return err log-order) ;; side-effect: auto-wrapped
Thread-first through Results with smart-wrap. Each step receives the
unwrapped :ok value. If a step returns a Result, use it directly (bind).
If it returns a plain value, auto-wrap in ok (fmap). Short-circuits on
first error.
Like some-> but with Result-based error info instead of nil.
(ok-> (validate-order order catalog) ;; switch: may return err
price-order ;; pure: auto-wrapped in ok
(acknowledge create-letter) ;; switch: may return err
log-order) ;; side-effect: auto-wrapped(ok->> expr & forms)Thread-last through Results with smart-wrap. Like ok-> but threads the unwrapped value in last position.
(ok->> (ok [1 2 3 4]) (map inc) (reduce +))
Thread-last through Results with smart-wrap. Like ok-> but threads the
unwrapped value in last position.
(ok->> (ok [1 2 3 4])
(map inc)
(reduce +))(on-error handler-fn result)Middleware: inspect rescue result, call handler-fn if error metadata present. Composes with rescue — rescue captures, on-error reacts.
(on-error (fn [{:keys [message form]}] (log/error "failed:" message)) (rescue {} (risky-call)))
handler-fn receives: {:message "..." :form "(risky-call)"} Returns the rescue result unchanged (transparent middleware).
Middleware: inspect rescue result, call handler-fn if error metadata present.
Composes with rescue — rescue captures, on-error reacts.
(on-error (fn [{:keys [message form]}] (log/error "failed:" message))
(rescue {} (risky-call)))
handler-fn receives: {:message "..." :form "(risky-call)"}
Returns the rescue result unchanged (transparent middleware).(rescue fallback & body)Supervision boundary — catch ANY throwable, return fallback.
Like Erlang's catch Expr: never let a failure propagate.
Error context attached via Clojure metadata, not logging.
(rescue [] (traverse ids)) ;; => [] on failure, error in ^{::error {...}} (rescue nil (get-entry id)) ;; => nil on failure (nil can't carry meta) (rescue {} (compute-stats data)) ;; => {} on failure, (::error (meta result)) for details
Error data shape: {::error {:message "..." :form "(traverse ids)"}}
Supervision boundary — catch ANY throwable, return fallback.
Like Erlang's `catch Expr`: never let a failure propagate.
Error context attached via Clojure metadata, not logging.
(rescue [] (traverse ids)) ;; => [] on failure, error in ^{::error {...}}
(rescue nil (get-entry id)) ;; => nil on failure (nil can't carry meta)
(rescue {} (compute-stats data)) ;; => {} on failure, (::error (meta result)) for details
Error data shape: {::error {:message "..." :form "(traverse ids)"}}(rescue-fn f)(rescue-fn f fallback)Wrap f: on throwable return fallback (default nil). For keep/map pipelines. Eliminates (keep (fn [x] (try (f x) (catch Exception _ nil))) coll).
(keep (rescue-fn #(parse %)) items) ;; nil on error → filtered by keep (map (rescue-fn #(parse %) :not-found) items) ;; :not-found on error
Wrap f: on throwable return fallback (default nil). For keep/map pipelines. Eliminates (keep (fn [x] (try (f x) (catch Exception _ nil))) coll). (keep (rescue-fn #(parse %)) items) ;; nil on error → filtered by keep (map (rescue-fn #(parse %) :not-found) items) ;; :not-found on error
(rescue-interrupt label fallback & body)Like rescue-log, but treats InterruptedException as a silent
cancellation signal: re-interrupts the current thread and returns
fallback WITHOUT logging. Other throwables fall through to
rescue-log semantics (log + fallback).
On cljs there is no thread interruption — degrades to rescue-log.
(rescue-interrupt "query-axioms-worker" [] (query-scoped-entries store q))
Like `rescue-log`, but treats `InterruptedException` as a silent cancellation signal: re-interrupts the current thread and returns `fallback` WITHOUT logging. Other throwables fall through to `rescue-log` semantics (log + fallback). On cljs there is no thread interruption — degrades to `rescue-log`. (rescue-interrupt "query-axioms-worker" [] (query-scoped-entries store q))
(rescue-log label fallback & body)Like rescue, but logs the exception via clojure.tools.logging/warn
under label before returning fallback. Combines rescue + log +
error-map reshape into one form. Replaces the common pattern of
(try body (catch Exception e (log/warn e ...) fallback)).
(rescue-log "ensure-require" nil (risky-op)) ;; => (risky-op) result on success, nil + warn-log on failure
label — short call-site identifier, appears in log output
fallback — returned on any throwable caught
Logger degrades silently when clojure.tools.logging is absent (always on cljs).
Like `rescue`, but logs the exception via `clojure.tools.logging/warn` under `label` before returning `fallback`. Combines rescue + log + error-map reshape into one form. Replaces the common pattern of `(try body (catch Exception e (log/warn e ...) fallback))`. (rescue-log "ensure-require" nil (risky-op)) ;; => (risky-op) result on success, nil + warn-log on failure `label` — short call-site identifier, appears in log output `fallback` — returned on any throwable caught Logger degrades silently when clojure.tools.logging is absent (always on cljs).
(resolve-warn-fn)Best-effort lookup of clojure.tools.logging/warn. Returns fn or nil. Public because rescue-log/rescue-interrupt macros expand to call sites that resolve this var in the caller's namespace. On cljs there is no runtime var resolution; returns nil (logger degrades silently).
Best-effort lookup of clojure.tools.logging/warn. Returns fn or nil. Public because rescue-log/rescue-interrupt macros expand to call sites that resolve this var in the caller's namespace. On cljs there is no runtime var resolution; returns nil (logger degrades silently).
(try-effect & body)Execute body in try/catch, returning ok on success or err on exception. Category defaults to :effect/exception.
(try-effect (do-side-effect!)) => (ok result) or (err :effect/exception {:message "..."})
Execute body in try/catch, returning ok on success or err on exception.
Category defaults to :effect/exception.
(try-effect (do-side-effect!))
=> (ok result) or (err :effect/exception {:message "..."})(try-effect* category & body)Like try-effect but with a custom error category.
(try-effect* :io/read-failure (slurp path)) => (ok content) or (err :io/read-failure {:message "..."})
Like try-effect but with a custom error category.
(try-effect* :io/read-failure (slurp path))
=> (ok content) or (err :io/read-failure {:message "..."})(try-effect-throwable* category & body)Like try-effect* but catches ANY Throwable (not just Exception) on the JVM.
Use ONLY at supervision boundaries where an Error must degrade to an err
Result instead of aborting — e.g. addon/manifest loading, where a
NoClassDefFoundError/LinkageError at require time or an AssertionError
from a :pre/:post/assert/slot :validate would otherwise skip every
later step. Returns the same ok/err shape as try-effect*, so ok?/:ok
work unchanged.
Contract: the caller MUST re-log the returned :class. Do NOT use this to
silently swallow a genuine OutOfMemoryError — inspect :class and re-raise
or surface fatal Errors rather than treating them as recoverable.
(try-effect-throwable* :addon/load-failed (require ns-sym)) => (ok result) or (err :addon/load-failed {:message "..." :class "..."})
Like try-effect* but catches ANY Throwable (not just Exception) on the JVM.
Use ONLY at supervision boundaries where an Error must degrade to an err
Result instead of aborting — e.g. addon/manifest loading, where a
NoClassDefFoundError/LinkageError at `require` time or an AssertionError
from a `:pre`/`:post`/`assert`/slot `:validate` would otherwise skip every
later step. Returns the same ok/err shape as `try-effect*`, so `ok?`/`:ok`
work unchanged.
Contract: the caller MUST re-log the returned `:class`. Do NOT use this to
silently swallow a genuine OutOfMemoryError — inspect `:class` and re-raise
or surface fatal Errors rather than treating them as recoverable.
(try-effect-throwable* :addon/load-failed (require ns-sym))
=> (ok result) or (err :addon/load-failed {:message "..." :class "..."})(with-error-handler rescue-wrapped-fn handler-fn)HOF middleware: wrap a rescue-fn with an error callback. Creates a composed fn that calls handler-fn on rescue failures.
(def safe-parse (with-error-handler (rescue-fn #(parse %) nil) (fn [{:keys [message]}] (log/warn "parse failed:" message)))) (safe-parse input)
HOF middleware: wrap a rescue-fn with an error callback.
Creates a composed fn that calls handler-fn on rescue failures.
(def safe-parse (with-error-handler
(rescue-fn #(parse %) nil)
(fn [{:keys [message]}] (log/warn "parse failed:" message))))
(safe-parse input)cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |