such.better-doc
Requiring this file will replace some clojure.core
docstrings with better versions.
I’d be ecstatic if documentation like this, or derived from this, were included in clojure.core. Note that the Unlicense allows anyone to do that.
butlast
added in 1.0
(butlast coll)
Return a seq of all but the last item in coll, in linear time. If you are working on a vector and want the result to be a vector, use pop
.
cond->
macro
added in 1.5
(cond-> expr & clauses)
The clauses
have two parts: a test expression and a form. An entire cond->
expression looks like this:
(cond-> <expr>
<independent-test-1> <exec-1>
<independent-test-2> <exec-2>
...)
The independent tests do not have the value of expr
threaded into them. If, however, independent-test-1
is truthy, the value of expr
will be threaded into exec-1
, using the rules of ->
. The resulting value will be threaded into the value of exec2
when independent-test-2
is truthy.
Examples will clarify. Here is a cond->
form that threads through each of the exec
forms:
(cond-> 1
true inc
true inc
true inc)
=> 4
Here’s an example that illustrates that the original expr
has nothing to do with the tests:
(cond-> 1
string? inc
string? inc
string? inc)
=> 4
The result is 4 because string?
, a function, is a truthy value.
Here’s an example of a function that takes arguments describing which branches to take:
(defn brancher [& branches]
(letfn [(take? [n] (contains? (set branches) n))]
(cond-> []
(take? 1) (conj 1)
(take? 2) (conj 2)
(take? 3) (conj 3))))
(brancher 1 3) => [1 3]
cond->>
macro
added in 1.5
(cond->> expr & clauses)
This is like cond->, except that values are threaded into the last position, as with ->>
.
(cond->> [1 2 3]
false (map inc) ; not taken
true (map -))
=> [-1 -2 -3]
every-pred
added in 1.3
(every-pred p)
(every-pred p1 p2)
(every-pred p1 p2 p3)
(every-pred p1 p2 p3 & ps)
Take N functions and produce a single predicate - call it result
. result
will return true
iff each of the original functions returns a truthy value. result
evaluates the functions in order, and the first one that produces a falsey value stops any further checking. In that case, false
is returned.
See also some-fn.
( (every-pred integer? even? pos?) "hi") => false
( (every-pred integer? even? pos?) 4) => true
find-ns
added in 1.0
(find-ns sym)
sym
must be a symbol. If it names an existing namespace, that namespace is returned. Otherwise, nil
is returned. Other examples
(find-ns 'clojure.core) => #<Namespace clojure.core>
map-indexed
added in 1.2
(map-indexed f)
(map-indexed f coll)
In the two-argument form, the following two are equivalent:
(map-indexed f coll)
(map f (range) coll)
Thus, f
should accept two arguments: the index of an element in coll
and the element itself.
(map-indexed vector [:a :b :c]) => ([0 :a] [1 :b] [2 :c])
Like map
, map-indexed
is lazy.
The single argument form produces a transducer.
ns-name
added in 1.0
(ns-name ns)
ns
must be either a namespace or a symbol naming a namespace. If the namespace exists, its symbol-name is returned. If not, an exception is thrown. Note: the more common name
function cannot be applied to namespaces. Other examples
(ns-name *ns*) => 'such.better-doc
ns-resolve
added in 1.0
(ns-resolve ns sym)
(ns-resolve ns env sym)
ns-resolve
goes from a symbol to the var or class it represents. Other examples
The first (ns
) argument must be either a namespace or a symbol naming a namespace (e.g., 'clojure.core
). The final argument sym
must be a symbol. There are four cases for that final argument:
sym
is not namespace qualified (e.g.,'even?
), and you hope it corresponds to a var inns
. If there is a var that (1) is “available” inns
and (2) has the same name assym
, it is returned. Otherwise,nil
is returned. “Available” means the var has either beenintern
ed in the namespace orrefer
ed into it.(ns-resolve *ns* 'a-var) => #'this.namespace/a-var (ns-resolve *ns* 'even?) => #'clojure.core/even? (ns-resolve 'clojure.core 'even?) => #'clojure.core/even?
sym
is a namespace-qualified symbol (e.g.,'clojure.core/even?
) that you hope corresponds to a var. The behavior is the same as (1), except thatns
is not used. The symbol’s namespace is used instead.(ns-resolve *ns* 'clojure.core/even?) => #'clojure.core/even?
Note: Even though the
ns
argument is not used in the lookup, it must still either be a namespace or a symbol that names an existing namespace. If not, an exception will be thrown.Because
ns
is unused,resolve
is better for this case.sym
is a fully qualified class name (e.g.,'java.lang.Object
). If such a class exists, it is returned. Otherwise, aClassNotFoundException
is thrown. Thens
argument is ignored, except that it must be a namespace or a symbol naming one.(ns-resolve *ns* 'java.lang.Object) => java.lang.Object
Because
ns
is unused,resolve
is better for this case.sym
is a symbol you hope names a classimport
ed intons
. If there is a class with that (unqualified) name inns
, it is returned.(ns-resolve 'clojure.core 'Object) => java.lang.Object (import 'java.util.AbstractCollection) (ns-resolve *ns* 'AbstractCollection) => java.util.AbstractCollection
If the class hasn’t been imported, the function returns
nil
(rather than throwing an exception, as in the fully-qualified case).(ns-resolve 'clojure.core 'AbstractMethodHandlerFactoryFactory) => nil (ns-resolve 'clojure.core 'java.lang.AbstractMethodHandlerFactoryFactory) => (throws)
In the three-argument case, the second env
argument is a map whose keys must be symbols. If any of the keys are =
to the final argument, nil
is returned (instead of a match, if any).
(ns-resolve *ns* 'even?) => #'clojure.core/even?
(ns-resolve *ns* '{even? "irrelevant"} 'even?) => nil
(ns-resolve *ns* 'Object) => java.lang.Object
(ns-resolve *ns* '{Object "irrelevant"} 'Object) => nil
reduce
added in 1.0
(reduce f coll)
(reduce f val coll)
reduce
converts a collection into a single value. Except for one exception (described below), f
must take two arguments. +
takes two arguments, so it can be used to reduce a collection of numbers to their sum:
(reduce + 0 [1 2 3 4]) => 10
+
is called four times. Here is the sequence of calls.
(+ 0 1) => 1 ; sum of first element
(+ 1 2) => 3 ; sum of first two elements
(+ 3 3) => 6 ; sum of first three elements
(+ 6 4) => 10 ; ...
At any point, the first argument to f
is the “reduction” of all the previous calls to f
, and the second argument is the next collection element to add into the reduction. When defining functions to use with reduce
, the first argument is often called acc
(for “accumulator”) or so-far
.
When using +
, the distinction between the two arguments isn’t clear, so here’s an example that returns the longest string in a collection:
(reduce (fn [max-so-far elt]
(if (> (count elt) max-so-far)
(count elt)
max-so-far))
0
["abc" "ab" "abcd" "a"]) => 4
When you’re surprised by the results of a call to reduce
, you can use reductions as an easy way to see what’s going on:
(reductions + 0 [1 2 3 4])
=> (0 1 3 6 10)
reductions
returns a collection of all the first arguments to f
plus the final result.
Reduce is lazy, so the reduction isn’t done until the result is used.
The two-argument form can be used when (f val (first coll))
is the same as (first coll)
. That’s the case with +
, where (+ 0 1)
is 1
. So, in the two argument form, the first call to f
uses the first argument of the collection as the starting reduction and begins working with the second element:
(reduce + [1 2 3 4]) => 10
(reductions + [1 2 3 4]) => (1 3 6 10) ; slightly different result
Small arrays: f
takes two values. What if there aren’t two values to give it? There’s one special case for the three-argument form:
(reduce + 0 []) => 0
(reductions + 0 []) => (0)
In this case, val
is returned and f
is never called.
There are two special two-argument cases. The first is when there’s only one element in the collection:
(reduce + [10]) => 10
The handling is really the same as the above, since the first argument in the collection is treated as the starting val
. More interesting is the empty collection:
(reduce + []) => 0
(reductions + []) => (0)
Here, f
is called, but with zero arguments. It happens that (+)
is 0
. In the longest-string example, though, the result is not so pretty:
(reduce (fn [max-so-far elt]...) [])
ArityException Wrong number of args (0) ...
See also: reductions, reduce-kv
reduce-kv
added in 1.4
(reduce-kv f init coll)
For maps, reduce-kv
is a trivial variant on reduce. The following two functions are the same:
(reduce (fn [so-far [key val]] ...) ... {...}
(reduce-kvs (fn [so-far key val ] ...) ... {...}
For vectors, reduce-kv
is to reduce
as map-indexed is to map
: it provides indexes as well as collection elements. The argument list is [reduction-so-far index element]
.
This example sums up the indices and values of a vector:
(reduce-kv + 0 [-1 -2 -3 -4]) => -4
It works because +
allows three arguments. Here’s an example that converts a vector to a map whose keys are the vector indexes:
(reduce-kv (fn [acc i elt] (assoc acc i elt))
{}
["0" "2" "3" "4"])
=> {0 "0", 1 "2", 2 "3", 3 "4"}
If coll
is empty, init
is returned and f
is not called.
reductions
added in 1.2
(reductions f coll)
(reductions f init coll)
Perform a reduce but don’t return only the final reduction. Return a sequence of the intermediate reductions, ending with the final reduction.
Consider this reduction that produces nested vectors:
(reduce (fn [so-far elt] (vector (conj so-far elt)))
[]
[1 2 3 4 5])
=> [[[[[[1] 2] 3] 4] 5]]
Using reductions
instead:
(reductions (fn [so-far elt] (vector (conj so-far elt)))
[]
[1 2 3 4 5])
=> ([]
[[1]]
[[[1] 2]]
[[[[1] 2] 3]] [[[[[1] 2] 3] 4]] [[[[[[1] 2] 3] 4] 5]])
To be more precise, the result is a sequence of the first arguments to f
, followed by the final value. Those are slightly different in the two-argument and three-argument cases:
(reductions + 0 [1 2 3]) => (0 1 3 6)
(reductions + [1 2 3]) => ( 1 3 6)
The sequence is lazy:
(take 3 (reductions (fn [so-far elt] (vector (conj so-far elt)))
[]
(range))) ; infinite list here
=> ([] [[0]] [[[0] 1]])
sequential?
added in 1.0
(sequential? coll)
True of lazy sequences, vectors, and lists. False for other Clojure built-in types. Note: perhaps surprisingly, not true of strings and java arrays. Any new type can be sequential?
if it implements the Java interface Sequential
, a marker interface that has no methods.
some
added in 1.0
(some pred coll)
Apply pred
to each element in coll
until it returns a truthy value, then return that value (*not* the element). This can be used to ask the question “is there an even value in the collection?”:
(some even? [1 2 3 4]) => true
However, as signaled by the lack of a ‘?’ in some
, its value is not necessarily a boolean. Here is how you would ask “is there a value greater than zero and, if so, what is the first of them?”:
(some #(and (pos? %) %) [-1 -3 2 4]) => 2
some
is often used to ask the question “is X in the collection?”. That takes advantage of how sets can be treated as a “contains value?” function:
(#{1 2 3} 2) => 2
(#{1 2 3} -88) => nil
So:
(some #{2} [1 2 3]) => 2
(some #{2} [-1 -2 -3]) => nil
You may find any? (in such.shorthand
) easier to remember.
It’s easy to forget that some
returns the value of the predicate, not the element itself. To be sure you get the element, do this:
(first (filter pred coll))
That’s find-first in such.shorthand
.
some-fn
added in 1.3
(some-fn p)
(some-fn p1 p2)
(some-fn p1 p2 p3)
(some-fn p1 p2 p3 & ps)
Take N functions and produce a single function - call it result
. result
evaluates the N functions in order. When one returns a truthy result, result
skips the remaining functions and returns that result. If none of the functions returns a truthy value, result
returns a falsey value.
This function is called some-fn
because it does not produce a pure (true/false) result. See also every-pred.
( (some-fn even? pos?) 3) => true
( (some-fn even? pos?) -3) => false
( (some-fn second first) [1 2 3]) => 2
( (some-fn second first) [1]) => 1
( (some-fn second first) []) => nil
symbol
added in 1.0
(symbol name)
(symbol ns name)
Creates a symbol from its arguments, which must be strings. The name of that result is name
. In the one-argument version, the result’s namespace is nil
. In the two-argument version, it’s ns
. Note that ns
need not refer to an existing namespace:
(symbol "no.such.namespace" "the") => 'no.such.namespace/the