Numbers and their limits

Clojure’s type system is strong: applying an operation to an improper type fails rather than silently misbehaving. It is also dynamic, meaning type errors surface when code runs, not when it is first read. The core numeric types mirror Java’s primitives, since Clojure runs on the JVM.

The default integer type is java.lang.Long, a signed 64-bit value using two’s complement representation. The sign takes one bit, leaving 63 for magnitude, so values range from -263 to 263 - 1. That upper bound is roughly 9.2 quintillion, and most computations never exceed it. If you try, Clojure refuses rather than wrapping around:

user=> (inc Long/MAX_VALUE)

ArithmeticException integer overflow  clojure.lang.Numbers.throwIntOverflow (Numbers.java:1388)

The ArithmeticException with the message “integer overflow” is Clojure’s protection against corrupt arithmetic. Should you genuinely need larger values, the arbitrary-precision BigInt type is available — note the trailing N in its printed form:

user=> (type 5N)
clojure.lang.BigInt

There are also smaller fixed-width integer types inherited from Java: Integer (32-bit), Short (16-bit), and Byte (8-bit), with maxima of 231-1, 215-1, and 27-1 respectively. Clojure’s literal syntax handles the conversion automatically when a value fits a smaller type.

user=> Integer/MAX_VALUE
2147483647
user=> Short/MAX_VALUE
32767
user=> Byte/MAX_VALUE
127

Fractions and approximation

Non-integer values in Clojure default to Double, a 64-bit floating-point type. Floats (32-bit) also exist, but doubles are the norm. Floating-point values are approximations — they represent small numbers finely and large numbers coarsely, which can produce surprising results:

user=> 0.99999999999999999
1.0

When an exact fraction is required, Clojure has a rational Ratio type. Dividing two integers produces a ratio when the result is not a whole number:

user=> (type 1/3)
clojure.lang.Ratio

Arithmetic operators aim to preserve information: adding two longs stays a long, but mixing a double into an operation promotes the result to a double. Equality is similarly careful. The equality predicate = considers integers and floating-point numbers distinct, while == compares mathematical values regardless of representation:

user=> (= 3 3.0)
false
user=> (== 3 3.0)
true

Subtraction, multiplication, and division use -, *, and /. Placing the operator first allows chaining multiple arguments in one call. With more than two arguments, subtraction and division apply the operation successively, starting from the first value. A single argument returns that argument unchanged, and zero arguments return the additive or multiplicative identity — a generalization that becomes useful in higher-order numeric code:

user=> (+)
0
user=> (*)
1

Comparisons accept multiple values and assert an ordering: <= checks non-decreasing order, < strictly increasing, and > and >= the descending counterparts. Equality also works across more than two arguments. For incrementing and decrementing by one, inc and dec are the compact helpers.

user=> (inc 5)
6
user=> (dec 5)
4

Strings and text patterns

Text values, or strings, are also Java-backed — specifically java.lang.String. The universal converter str turns nearly anything into its string representation; for nil, that representation is the empty string. Used with multiple arguments, str concatenates them into one string:

user=> (str "meow " 3 " times")
"meow 3 times"

Pattern matching in text relies on regular expressions, written with a #"..." reader literal. The function re-find searches a string for the first match of a pattern, while re-matches requires the entire string to match. The latter captures parenthesized groups, returning them alongside the full match:

user=> (rest (re-matches #"(.+):(.+)" "mouse:treat"))
("mouse" "treat")

Regular expressions form a language of their own, shared across most programming languages, so there is no need to master them upfront — just reach for a reference when a specific search or extraction arises.

Truth values

Clojure treats only two values as false in conditional contexts: false and nil. Every other value — numbers, strings, keywords, even the number zero — counts as true. This is a deliberate departure from C-style languages where zero is falsy; Lisp draws the line at the special values. You can test the truthiness of any expression directly with the boolean function if needed.

The logical operators work with these rules. and returns the first falsy value, or the last value if all are truthy. Conversely, or returns the first truthy value. not inverts a value’s logical sense: it yields true for a falsy input and false for a truthy one.

user=> (not 2)
false
user=> (not nil)
true

These operators short-circuit their evaluation, examining arguments from left to right and stopping early when the outcome is already determined. That behavior will become significant when they are used to control program flow.

Names for things

Symbols are bare names — foo, str, or +. They refer to values: when evaluated, a symbol is looked up and replaced with what it points to. A symbol can also carry a fully qualified name, separating the namespace from the local name with a slash, such as clojure.core/str. The same separation appears in global constants like java.lang.Long/MAX_VALUE.

Closely related are keywords, which begin with a colon. While symbols are references that resolve to other values, a keyword is just a name — it evaluates to itself. Keywords serve as the idiomatic way to attach labels to data, and, crucially, they can act as lookup functions when applied to collections:

user=> (type :cat)
clojure.lang.Keyword
user=> (str :cat)
":cat"
user=> (name :cat)
"cat"

That ability to pull a value out of a map by its label is a staple of Clojure data manipulation, and it forms the groundwork for working with the language’s core data structures.

Ordered collections: lists and vectors

A collection is a container that groups values, which we call its elements or members. In the previous chapter we met one such container—the list. Lists are written with parentheses and are quoted with a ' to keep them from being evaluated; you can also build one explicitly with list.

user=> '(1 2 3)
(1 2 3)
user=> (type '(1 2 3))
clojure.lang.PersistentList
user=> (list 1 2 3)
(1 2 3)

Like every other value, lists can be compared for equality:

user=> (= (list 1 2) (list 1 2))
true

You can add an element to a list with conj:

user=> (conj '(1 2 3) 4)
(4 1 2 3)

Notice that the new element landed at the front, not the back. That is a direct consequence of the underlying representation: lists are stored as linked chains, where each link holds a value and a pointer to the next link. Reaching the first element is immediate, but each subsequent element costs an extra step down the chain.

user=> (first (list 1 2 3))
1
user=> (second (list 1 2 3))
2
user=> (nth (list 1 2 3) 2)
3

To fetch an element at a given position, use nth, where the first element is index 0, the second is index 1, and so on. Because of the linked structure, nth on longer lists is slow. For fast random access to any element, we reach for vectors.

Vectors use square brackets instead of parentheses. Since they are not evaluated the way lists are, no quoting is required:

user=> [1 2 3]
[1 2 3]
user=> (type [1 2 3])
clojure.lang.PersistentVector

The vector function builds one from scratch, and vec converts any other collection into a vector:

user=> (vector 1 2 3)
[1 2 3]
user=> (vec (list 1 2 3))
[1 2 3]

On a vector, conj appends to the end rather than the beginning:

user=> (conj [1 2 3] 4)
[1 2 3 4]

The familiar first, second, and nth all work on vectors too, and nth in particular is fast. The reason is structural: a vector is internally a very broad, shallow tree, with each node branching into 32 smaller pieces. Even for large vectors, only a few hops are needed to reach any element.

Besides first, it is common to want everything after the head of a collection. Two functions both give you that rest of a collection:

user=> (rest [1 2 3])
(2 3)
user=> (next [1 2 3])
(2 3)

rest and next differ only in their treatment of the empty case:

user=> (rest [1])
()
user=> (next [1])
nil

Here rest yields logical true (it returns an empty sequence, which is truthy), while next gives logical false by returning nil. In practice both are interchangeable in most code.

To get the final element, use last:

user=> (last [1 2 3])
3

And count tells you how many elements a vector holds:

user=> (count [1 2 3])
3

Because vectors are built for index lookup, you can also treat a vector as a function of its index:

user=> ([:a :b :c] 1)
:b

Here we ask the vector of three keywords for the element at index 1, which gives :b. Counting starts at zero, as in most languages.

One more convenience: a vector and a list holding the same elements in the same order compare as equal:

user=> (= '(1 2 3) [1 2 3])
true

In nearly all contexts, lists and vectors are interchangeable; their distinguishing traits are only performance and a few structure-specific operations.

Sets: unordered membership

When the question is “does this collection contain the number 3?” rather than “what is the third element?”, a set is the right tool. Sets are unordered collections of distinct values, written with #{...}:

user=> #{:a :b :c}
#{:a :c :b}

The elements came out in a scrambled order relative to how we wrote them. That ordering is not stable, so if you need a fixed order you can convert the set to a list or vector:

user=> (vec #{:a :b :c})
[:a :c :b]

Or ask for a sorted version of its elements:

(sort #{:a :b :c})
(:a :b :c)

As with other collections, conj inserts a new element into a set:

user=> (conj #{:a :b :c} :d)
#{:a :c :b :d}
user=> (conj #{:a :b :c} :a)
#{:a :c :b}

A set can never hold a value twice, so adding an element that is already present changes nothing. Removal is done with disj:

user=> (disj #{"hornet" "hummingbird"} "hummingbird")
#{"hornet"}

The most common test on a set is membership, handled by contains?:

user=> (contains? #{1 2 3} 3)
true
user=> (contains? #{1 2 3} 5)
false

As with vectors, a set can act as a verb on its own elements. Instead of returning a boolean, this form returns the element when it is present, or nil when it is not:

user=> (#{1 2 3} 3)
3
user=> (#{1 2 3} 4)
nil

Any collection can be turned into a set with set:

user=> (set [:a :b :c])
#{:a :c :b}

Maps: keys to values

The final collection type is the map, which associates keys with values. Its uses range from dictionaries—words to definitions—to records with named fields:

user=> {:name "mittens" :weight 9 :color "black"}
{:weight 9, :name "mittens", :color "black"}

Maps are enclosed in braces {...} with alternating keys and values. In the map above, the keys :name, :color, and :weight map to the values "mittens", "black", and 9. Look up a key with get:

user=> (get {"cat" "meow" "dog" "woof"} "cat")
"meow"
user=> (get {:a 1 :b 2} :c)
nil

In addition, get accepts a default value that is returned in place of nil when the key is absent:

user=> (get {:glinda :good} :wicked :not-here)
:not-here

A map itself can serve as a verb for lookups, since that is its core function:

user=> ({"amlodipine" 12 "ibuprofen" 50} "ibuprofen")
50

Keywords work as verbs too, searching for themselves inside a map:

user=> (:raccoon {:weasel "queen" :raccoon "king"})
"king"

Values are added or updated with assoc:

user=> (assoc {:bolts 1088} :camshafts 3)
{:camshafts 3 :bolts 1088}
user=> (assoc {:camshafts 3} :camshafts 2)
{:camshafts 2}

assoc inserts a new key when it is missing, and replaces the value in the map when the key is already there. Associating onto nil builds a fresh map one entry at a time:

user=> (assoc nil 5 2)
{5 2}

Several maps can be joined with merge, which combines all entries and resolves conflicts in favor of maps listed later:

user=> (merge {:a 1 :b 2} {:b 3 :c 4})
{:c 4, :a 1, :b 3}

To drop keys, use dissoc:

user=> (dissoc {:potatoes 5 :mushrooms 2} :mushrooms)
{:potatoes 5}

Composing larger values

These collections and primitive types nest freely, which is how we faithfully model real domains. A person, a recipe, and even a time series of national statistics can each be captured as nested combinations of maps, vectors, and keywords:

{:name "Amelia Earhart"
 :birth 1897
 :death 1939
 :awards {"US"    #{"Distinguished Flying Cross" "National Women's Hall of Fame"}
          "World" #{"Altitude record for Autogyro" "First to cross Atlantic twice"}}}
{:title "Chocolate chip cookies"
 :ingredients {"flour"           [(+ 2 1/4) :cup]
               "baking soda"     [1   :teaspoon]
               "salt"            [1   :teaspoon]
               "butter"          [1   :cup]
               "sugar"           [3/4 :cup]
               "brown sugar"     [3/4 :cup]
               "vanilla"         [1   :teaspoon]
               "eggs"            2
               "chocolate chips" [12  :ounce]}}
{"Afghanistan" {2008 27.8}
 "Indonesia"   {2008 34.1 2010 35.6 2011 38.1}
 "Uruguay"     {2008 46.3 2009 46.3 2010 45.3}}

Clojure’s answer to complexity is composition: we build rich data structures out of simpler ones, then mine them with tools like first, nth, get, and contains?, and shape them with conj, disj, assoc, and dissoc. Between the types for individual values and the assembly patterns of collections, we have enough vocabulary to write a wide range of programs.

There is, however, one remaining type of value, and it is the one we have been using to inspect all the others. What is type itself? What are these verbs we have practiced, and where did they come from?

user=> (type type)
clojure.core$type

The answer is the subject of [chapter three: functions](http://aphyr.com/posts/303-clojure-from-the-ground-up-functions).