Unorthodox control flow.
Unorthodox control flow.
(assert+ x)
(assert+ x message)
(assert+ x ex-type message)
Like Clojure assert, but throws customizable exceptions (by default, IllegalArgumentException), and returns the value it checks, instead of nil.
Clojure assertions are a little weird. Syntactically, they're a great candidate for runtime validation of state--making sure you got an int instead of a map, or that an object you looked up was present. However, they don't return the thing you pass them, which makes it a bit akward to use them in nested expressions. You typically have to do a let binding and then assert. So... let's return truthy values! Now you can
(assert+ (fetch-person-from-db :liu)
"Couldn't fetch Liu!")
Moreover, Clojure assertions sensibly throw AssertionError. However, AssertionError is an error that "should never occur" and "a reasonable application should not try to catch." There are LOTS of cases where you DO expect assertions to fail sometimes and intend to catch them: for instance, validating user input, or bounds checks. So we're going to throw customizable exceptions.
Oh, and you can throw maps too. Those become ex-infos.
(assert+ (thing? that)
{:type :wasn't-a-thing
:I'm [:so :sorry]})
Like Clojure assert, but throws customizable exceptions (by default, IllegalArgumentException), and returns the value it checks, instead of nil. Clojure assertions are a little weird. Syntactically, they're a great candidate for runtime validation of state--making sure you got an int instead of a map, or that an object you looked up was present. However, they don't *return* the thing you pass them, which makes it a bit akward to use them in nested expressions. You typically have to do a let binding and then assert. So... let's return truthy values! Now you can (assert+ (fetch-person-from-db :liu) "Couldn't fetch Liu!") Moreover, Clojure assertions sensibly throw AssertionError. However, AssertionError is an error that "should never occur" and "a reasonable application should not try to catch." There are LOTS of cases where you DO expect assertions to fail sometimes and intend to catch them: for instance, validating user input, or bounds checks. So we're going to throw customizable exceptions. Oh, and you can throw maps too. Those become ex-infos. (assert+ (thing? that) {:type :wasn't-a-thing :I'm [:so :sorry]})
(bounded-future & body)
Like future, but runs on the bounded agent executor. Useful for CPU-bound futures.
Like future, but runs on the bounded agent executor. Useful for CPU-bound futures.
(bounded-future-call f)
Like clojure.core/future-call, but runs on the bounded agent executor instead of the unbounded one. Useful for CPU-bound futures.
Like clojure.core/future-call, but runs on the bounded agent executor instead of the unbounded one. Useful for CPU-bound futures.
(bounded-pmap f coll)
Like pmap, but spawns tasks immediately, and uses the global bounded agent threadpool. Ideal for computationally bound tasks, especially when you might want to, say, pmap inside each of several parallel tasks without spawning eight gazillion threads.
Like pmap, but spawns tasks immediately, and uses the global bounded agent threadpool. Ideal for computationally bound tasks, especially when you might want to, say, pmap *inside* each of several parallel tasks without spawning eight gazillion threads.
(disorderly a)
(disorderly a b)
(disorderly a b & more)
This is a chaotic do expression. Like do
, takes any number of forms. Where
do
evaluates forms in order, disorderly
evaluates them in a random order.
Where do
returns the result of evaluating the final form, disorderly
returns a sequence of the results of each form, in lexical (as opposed to
execution) order, making it suitable for binding results.
This is particularly helpful when you want side effects, but you're not exactly sure how. Consider, for instance, testing that several mutations of an object all commute.
(disorderly (do (prn 1) :a)
(do (prn 2) :b))
... prints either 1 then 2, or 2 then 1, but always returns (:a :b). Note
that disorderly
is not concurrent: branches evaluate in some order; it's
just not a deterministic one.
This is a chaotic do expression. Like `do`, takes any number of forms. Where `do` evaluates forms in order, `disorderly` evaluates them in a random order. Where `do` returns the result of evaluating the final form, `disorderly` returns a sequence of the results of each form, in lexical (as opposed to execution) order, making it suitable for binding results. This is particularly helpful when you want side effects, but you're not exactly sure how. Consider, for instance, testing that several mutations of an object all commute. (disorderly (do (prn 1) :a) (do (prn 2) :b)) ... prints either 1 then 2, or 2 then 1, but always returns (:a :b). Note that `disorderly` is *not* concurrent: branches evaluate in some order; it's just not a deterministic one.
(fcatch f)
Takes a function and returns a version of it which returns, rather than throws, exceptions.
; returns RuntimeException
((fcatch #(throw (RuntimeException. "hi"))))
Takes a function and returns a version of it which returns, rather than throws, exceptions. ; returns RuntimeException ((fcatch #(throw (RuntimeException. "hi"))))
(letr bindings & body)
Let bindings, plus early return.
You want to do some complicated, multi-stage operation assigning lots of variables--but at different points in the let binding, you need to perform some conditional check to make sure you can proceed to the next step. Ordinarily, you'd intersperse let and if statements, like so:
(let [res (network-call)]
(if-not (:ok? res)
:failed-network-call
(let [people (:people (:body res))]
(if (zero? (count people))
:no-people
(let [res2 (network-call-2 people)]
...
This is a linear chain of operations, but we're forced to nest deeply because we have no early-return construct. In ruby, we might write
res = network_call
return :failed_network_call if not x.ok?
people = res[:body][:people]
return :no-people if people.empty?
res2 = network_call_2 people
...
which reads the same, but requires no nesting thanks to Ruby's early return. Clojure's single-return is usually a boon to understandability, but deep linear branching usually means something like
This macro lets you write:
(letr [res (network-call)
_ (when-not (:ok? res) (return :failed-network-call))
people (:people (:body res))
_ (when (zero? (count people)) (return :no-people))
res2 (network-call-2 people)]
...)
letr works like let, but if (return x) is ever returned from a binding, letr returns x, and does not evaluate subsequent expressions.
If something other than (return x) is returned from evaluating a binding, letr binds the corresponding variable as normal. Here, we use _ to indicate that we're not using the results of (when ...), but this is not mandatory. You cannot use a destructuring bind for a return expression.
letr is not a true early return--(return x) must be a terminal expression for it to work--like (recur). For example,
(letr [x (do (return 2) 1)]
x)
returns 1, not 2, because (return 2) was not the terminal expression. Someone clever should fix this.
(return ...) only works within letr's bindings, not its body.
Let bindings, plus early return. You want to do some complicated, multi-stage operation assigning lots of variables--but at different points in the let binding, you need to perform some conditional check to make sure you can proceed to the next step. Ordinarily, you'd intersperse let and if statements, like so: (let [res (network-call)] (if-not (:ok? res) :failed-network-call (let [people (:people (:body res))] (if (zero? (count people)) :no-people (let [res2 (network-call-2 people)] ... This is a linear chain of operations, but we're forced to nest deeply because we have no early-return construct. In ruby, we might write res = network_call return :failed_network_call if not x.ok? people = res[:body][:people] return :no-people if people.empty? res2 = network_call_2 people ... which reads the same, but requires no nesting thanks to Ruby's early return. Clojure's single-return is *usually* a boon to understandability, but deep linear branching usually means something like - Deep nesting (readability issues) - Function chaining (lots of arguments for bound variables) - Throw/catch (awkward exception wrappers) - Monadic interpreter (slow, indirect) This macro lets you write: (letr [res (network-call) _ (when-not (:ok? res) (return :failed-network-call)) people (:people (:body res)) _ (when (zero? (count people)) (return :no-people)) res2 (network-call-2 people)] ...) letr works like let, but if (return x) is ever returned from a binding, letr returns x, and does not evaluate subsequent expressions. If something other than (return x) is returned from evaluating a binding, letr binds the corresponding variable as normal. Here, we use _ to indicate that we're not using the results of (when ...), but this is not mandatory. You cannot use a destructuring bind for a return expression. letr is not a *true* early return--(return x) must be a *terminal* expression for it to work--like (recur). For example, (letr [x (do (return 2) 1)] x) returns 1, not 2, because (return 2) was not the terminal expression. Someone clever should fix this. (return ...) only works within letr's bindings, not its body.
(letr-let-if groups body)
Takes a sequence of binding groups and a body expression, and emits a let for the first group, an if statement checking for a return, and recurses; ending with body.
Takes a sequence of binding groups and a body expression, and emits a let for the first group, an if statement checking for a return, and recurses; ending with body.
(letr-partition-bindings bindings)
Takes a vector of bindings [sym expr, sym' expr, ...]. Returns binding-groups: a sequence of vectors of bindgs, where the final binding in each group has an early return. The final group (possibly empty!) contains no early return.
Takes a vector of bindings [sym expr, sym' expr, ...]. Returns binding-groups: a sequence of vectors of bindgs, where the final binding in each group has an early return. The final group (possibly empty!) contains no early return.
(letr-rewrite-return expr)
Rewrites (return x) to (Return. x) in expr. Returns a pair of [changed? expr], where changed is whether the expression contained a return.
Rewrites (return x) to (Return. x) in expr. Returns a pair of [changed? expr], where changed is whether the expression contained a return.
(real-pmap f coll)
Like pmap, but spawns tasks immediately, and launches real Threads instead of using a bounded threadpool. Useful when your tasks might block on each other, and you don't want to deadlock by exhausting the default clojure worker threadpool halfway through the collection. For instance,
(let [n 1000
b (CyclicBarrier. n)]
(pmap (fn [i] [i (.await b)]) (range n)))
... deadlocks, but replacing pmap
with real-pmap
works fine.
If any thread throws an exception, all mapping threads are interrupted, and the original exception is rethrown. This prevents deadlock issues where mapping threads synchronize on some resource (like a cyclicbarrier or countdownlatch), but one crashes, causing other threads to block indefinitely on the barrier. Note that we do not include a ConcurrentExecutionException wrapper.
All pmap threads should terminate before real-pmap returns or throws. This prevents race conditions where mapping threads continue doing work concurrently with, say, clean-up code intended to run after the call to (pmap).
If the thread calling (pmap) itself is interrupted, all bets are off.
Like pmap, but spawns tasks immediately, and launches real Threads instead of using a bounded threadpool. Useful when your tasks might block on each other, and you don't want to deadlock by exhausting the default clojure worker threadpool halfway through the collection. For instance, (let [n 1000 b (CyclicBarrier. n)] (pmap (fn [i] [i (.await b)]) (range n))) ... deadlocks, but replacing `pmap` with `real-pmap` works fine. If any thread throws an exception, all mapping threads are interrupted, and the original exception is rethrown. This prevents deadlock issues where mapping threads synchronize on some resource (like a cyclicbarrier or countdownlatch), but one crashes, causing other threads to block indefinitely on the barrier. Note that we do not include a ConcurrentExecutionException wrapper. All pmap threads should terminate before real-pmap returns or throws. This prevents race conditions where mapping threads continue doing work concurrently with, say, clean-up code intended to run after the call to (pmap). If the thread calling (pmap) itself is interrupted, all bets are off.
(real-pmap-helper f coll)
Helper for real-pmap. Maps f over coll, collecting results and exceptions.
Returns a tuple of [results, exceptions], where results is a sequence of
results from calling f
on each element (nil
if f throws); and exceptions
is a sequence of exceptions thrown by f, in roughly time order.
Helper for real-pmap. Maps f over coll, collecting results and exceptions. Returns a tuple of [results, exceptions], where results is a sequence of results from calling `f` on each element (`nil` if f throws); and exceptions is a sequence of exceptions thrown by f, in roughly time order.
(with-retry initial-bindings & body)
It's really fucking inconvenient not being able to recur from within (catch) expressions. This macro wraps its body in a (loop [bindings] (try ...)). Provides a (retry & new bindings) form which is usable within (catch) blocks: when this form is returned by the body, the body will be retried with the new bindings. For instance,
(with-retry [attempts 5]
(network-request...)
(catch RequestFailed e
(if (< 1 attempts)
(retry (dec attempts))
(throw e))))
It's really fucking inconvenient not being able to recur from within (catch) expressions. This macro wraps its body in a (loop [bindings] (try ...)). Provides a (retry & new bindings) form which is usable within (catch) blocks: when this form is returned by the body, the body will be retried with the new bindings. For instance, (with-retry [attempts 5] (network-request...) (catch RequestFailed e (if (< 1 attempts) (retry (dec attempts)) (throw e))))
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close