This chapter will be a little introduction to ClojureScript without assumptions about previous knowledge of the Clojure language, providing a quick tour over all the things you will need to know about ClojureScript and understand the rest of this book.

This is practically all the syntax we need to know for using not only ClojureScript, but any Lisp. Being written in its own data structures (often referred to as homoiconicity) is a great property since the syntax is uniform and simple; also, code generation via macros is easier than in any other language, giving us plenty of power to extend the language to suit our needs.

ClojureScript uses more than lists for its syntax. The full details will be covered later, but here is an example of the usage of a vector (enclosed in brackets) for defining local bindings:

To distinguish function calls from lists of data items, we can quote lists to keep them from being evaluated. The quoted lists will be treated as data instead of as a function call:

In the example above, we’re applying the addition function + to the arguments 1 , 2 and 3 . ClojureScript allows many unusual characters like ? or - in symbol names, which makes it easier to read:

A list with a function in the first position is used for calling a function in ClojureScript. In the example below, we apply the addition function to three arguments. Note that unlike in other languages, + is not an operator but a function. Lisp has no operators; it only has functions.

Invented by John McCarthy in 1958, Lisp is one of the oldest programming languages that is still around. It has evolved into many derivatives called dialects, ClojureScript being one of them. It is a programming language written in its own data structures — originally lists enclosed in parentheses — but Clojure(Script) has evolved the Lisp syntax with more data structures, making it more pleasant to write and read.

In subsequent sections, we’ll go in depth about sets and the other collection types you’ve seen in this section.

Sets store zero or more unique items of any type and are unordered. Like maps, they use curly braces for their literal syntax, with the difference being that they use a # as the leading character. You can also use the set function to convert a collection to a set:

Like vectors, every item in a map literal is evaluated before the result is stored in a map, but the order of evaluation is not guaranteed.

Maps are a collection abstraction that allow you to store key/value pairs. In other languages, this type of structure is commonly known as a hash-map or dict (dictionary). Map literals in ClojureScript are written with the pairs between curly braces.

You can also explicitly create a vector with the vector function, but this is not commonly used in ClojureScript programs:

Like lists, vectors can contain objects of any type, as you can observe in the preceding example.

Like lists, vectors store a series of values, but in this case, with very efficient index access to their elements, as opposed to lists, which are evaluated in order. Don’t worry; in the following sections we’ll go in depth with details, but at this moment, this simple explanation is more than enough.

Lists have the peculiarity that they are very efficient if you access them sequentially or access their first elements, but a list is not a very good option if you need random (index) access to its elements.

As you can see, if you evaluate (inc 1) without prefixing it with ' , it will resolve the inc symbol to the inc function and will execute it with 1 as the first argument, returning the value 2 .

The following example shows the difference between a list without and with the preceding single quote mark:

As you can see, all list examples are prefixed with the ' char. This is because lists in Lisp-like languages are often used to express things like function or macro calls. In that case, the first item should be a symbol that will evaluate to something callable, and the rest of the list elements will be function arguments. However, in the preceding examples, we don’t want the first item as a symbol; we just want a list of items.

This is a classic collection type in languages based on Lisp. Lists are the simplest type of collection in ClojureScript. Lists can contain items of any type, including other collections.

ClojureScript comes with many types of collections. The main difference between ClojureScript collections and collections in other languages is that they are persistent and immutable.

Another big step in explaining a language is to explain its collections and collection abstractions. ClojureScript is not an exception to this rule.

Since the host language doesn’t contain character literals, ClojureScript characters are transformed behind the scenes into single character JavaScript strings.

One peculiar aspect of strings in ClojureScript is due to the language’s Lisp syntax: single and multiline strings have the same syntax:

In this case they are the same as in JavaScript:

There is almost nothing new we can explain about strings that you don’t already know. In ClojureScript, they work the same as in any other language. One point of interest, however, is that they are immutable.

Don’t worry if you don’t understand right away; symbols are used in almost all of our examples, which will give you the opportunity to learn more as we go on.

Symbols start with a non numeric character and can contain alphanumeric characters as well as *, +, !, -, _, ', and ? such as :

Symbols in ClojureScript are very, very similar to keywords (which you now know about). But instead of evaluating to themselves, symbols are evaluated to something that they refer to, which can be functions, variables, etc.

The keyword function has an arity-2 variant where we can specify the namespace as the first parameter:

Another alternative is to include the namespace in the keyword literal, this is useful when creating namespaced keywords for other namespaces:

When prefixing keywords with a double colon :: , the keyword will be prepended by the name of the current namespace. Note that namespacing keywords affects equality comparisons.

You can also create a keyword by calling the keyword function. Don’t worry if you don’t understand or are unclear about anything in the following example; functions are discussed in a later section.

As you can see, the keywords are all prefixed with : , but this character is only part of the literal syntax and is not part of the name of the object.

Keywords in ClojureScript are objects that always evaluate to themselves. They are usually used in map data structures to efficiently represent the keys.

As in any other language, numbers in ClojureScript are represented in the following ways:

In ClojureScript, numbers include both integers and floating points. Keeping in mind that ClojureScript is a guest language that compiles to JavaScript, integers are actually JavaScript’s native floating points under the hood.

ClojureScript embraces the host language, and where possible, uses the host’s provided types. For example: numbers and strings are used as is and behave in the same way as in JavaScript.

The ClojureScript language has a rich set of data types like most programming languages. It provides scalar data types that will be very familiar to you, such as numbers, strings, and floats. Beyond these, it also provides a great number of others that might be less familiar, such as symbols, keywords, regexes (regular expressions), vars, atoms, and volatiles.

Vars are always top level in the namespace ( which we will explain later ). If you use def in a function call, the var will be defined at the namespace level, but we do not recommend this - instead, you should use let to define variables within a function.

In ClojureScript, vars are represented by symbols and store a single value together with metadata.

ClojureScript is a mostly functional language that focuses on immutability. Because of that, it does not have the concept of variables as you know them in most other programming languages. The closest analogy to variables are the variables you define in algebra; when you say x = 6 in mathematics, you are saying that you want the symbol x to stand for the number six.

If a function only accepts one argument, you can omit the number after the % symbol, e.g., a function that squares a number: #(* %1 %1)) can be written #(* % %)) .

The %1 , %2 …​ %N are simple markers for parameter positions that are implicitly declared when the reader macro will be interpreted and converted to a fn expression.

ClojureScript provides a shorter syntax for defining anonymous functions using the #() reader macro (usually leads to one-liners). Reader macros are "special" expressions that will be transformed to the appropriate language form at compile time; in this case, to some expression that uses the fn special form.

The way to denote a variadic function is using the & symbol prefix on its arguments vector.

Another way to accept multiple parameters is defining variadic functions. Variadic functions are functions that accept an arbitrary number of arguments:

Here are some examples using the previously defined multi-arity function. Observe that if you call a function with the wrong number of arguments, the compiler will emit an error message.

This line: ([x] (myinc x 1)) says that if there is only one argument, call the function myinc with that argument and the number 1 as the second argument. The other function body ([x increment] (+ x increment)) says that if there are two arguments, return the result of adding them.

Let’s see an example, which will explain it better:

ClojureScript also comes with the ability to define functions with an arbitrary number of arguments. (The term arity means the number of arguments that a function takes.) The syntax is almost the same as for defining an ordinary function, with the difference that it has more than one body.

The string that comes between the function name and the parameter vector is called a docstring (documentation string); programs that automatically create web documentation from your source files will use these docstrings.

Let’s start creating named functions. But what does a named function really mean? It is very simple; in ClojureScript, functions are first-class and behave like any other value, so naming a function is done by simply binding the function to a symbol:

You can define a function and call it at the same time (in a single expression):

You can define an unnamed (anonymous) function with the fn special form. This is one type of function definition; in the following example, the function takes two parameters and returns their average.

The prefix notation has huge advantages, some of them not always obvious. ClojureScript does not make a distinction between a function and an operator; everything is a function. The immediate advantage is that the prefix notation allows an arbitrary number of arguments per "operator". It also completely eliminates the problem of operator precedence.

The + symbol represents an add function. It allows multiple parameters, whereas in ALGOL-type languages, + is an operator and only allows two parameters.

In the example above, inc is a function and is part of the ClojureScript runtime, and 1 is the first argument for the inc function.

If you want to know more about scopes, this Wikipedia article is very extensive and explains different types of scoping.

It’s time to make things happen. ClojureScript has what are known as first class functions. They behave like any other type; you can pass them as parameters and you can return them as values, always respecting the lexical scope. ClojureScript also has some features of dynamic scoping, but this will be discussed in another section.

The case branching expression has a similar use as our previous example with condp . The main differences are that case always uses the = predicate/function and its branching values are evaluated at compile time. This results in a more performant form than cond or condp but has the disadvantage that the condition value must be static.

The line condp = (keyword code) means that, in each of the following lines, ClojureScript will apply the = function to the result of evaluating (keyword code) .

Also, cond has another form, called condp , that works very similarly to the simple cond but looks cleaner when the condition (also called a predicate) is the same for all conditions:

Sometimes, the if expression can be slightly limiting because it does not have the "else if" part to add more than one condition. The cond macro comes to the rescue.

The block expression do can be used to have multiple expressions in an if branch. do is explained in the next section .

Let’s start with a basic one: if . In ClojureScript, the if is an expression and not a statement, and it has three parameters: the first one is the condition expression, the second one is an expression that will be evaluated if the condition expression evaluates to logical true, and the third expression will be evaluated otherwise.

ClojureScript has a very different approach to flow control than languages like JavaScript, C, etc.

This works because a set returns either the value itself for all contained elements or nil :

Jointly with the ability to implement the callable protocol (the IFn , explained more in detail later), data structures like sets can be used just as predicates, without need of additional wrapping them in a function:

This is the aspect where each language has its own semantics (mostly wrongly). The majority of languages consider empty collections, the integer 0, and other things like this to be false. In ClojureScript, unlike in other languages, only two values are considered as false: nil and false . Everything else is treated as logical true .

If you want just iterate and apply some side effectfull operation (like println ) over each item in the collection, you can just use the specialized function run! that internally uses fast reduction:

ClojureScript provides the doseq construct, which is analogous to for but executes the expression, discards the resulting values, and returns nil .

When we outlined the most common usages of the for construct in imperative programming languages, we mentioned that sometimes we want to run a computation for every value in a sequence, not caring about the result. Presumably we do this for achieving some sort of side-effect with the values of the sequence.

We can combine the modifiers shown above for expressing complex sequence generations or more clearly expressing the intent of our comprehension:

For filtering out generated values, use the :when modifier as in the following example:

We can use the :while modifier for expressing a condition that, when it is no longer met, will stop the sequence generation. Here’s an example:

Here’s an example of local bindings using the :let modifier; note that the bindings defined with it will be available in the expression:

We can also follow the bindings with three modifiers: :let for creating local bindings, :while for breaking out of the sequence generation, and :when for filtering out values.

for supports multiple bindings, which will cause the collections to be iterated in a nested fashion, much like nesting for loops in imperative languages. The innermost binding iterates “fastest.”

In this example, x is bound to each of the items in the vector [1 2 3] in turn, and returns a new sequence of two-item vectors with the original item squared.

for takes a vector of bindings and an expression and generates a sequence of the result of evaluating the expression. Let’s take a look at an example:

In ClojureScript, the for construct isn’t used for iteration but for generating sequences, an operation also known as "sequence comprehension". In this section we’ll learn how it works and use it to declaratively build sequences.

Remember to choose your starting value for the accumulator carefully. If you wanted to use reduce to find the product of a series of numbers, you would have to start with one rather than zero, otherwise all the numbers would be multiplied by zero!

We have not used the short syntax here because, although it requires less typing, it can be less readable, and when you are starting with a new language, it’s important to be able to read what you wrote! If you are comfortable with the short syntax, feel free to use it.

Here is a reduce that finds the total number of characters in a set of words:

Yet again, you can provide your own function as the first argument to reduce , but your function must have two parameters. The first one is the "accumulated value" and the second parameter is the collection item being processed. The function returns a value that becomes the accumulator for the next item in the list. For example, here is how you would find the sum of squares of a set of numbers (this is an important calculation in statistics). Using a separate function:

Converting an iterable to a single value, accumulating the intermediate result at every step of the iteration can be achieved with reduce , which takes a function for accumulating values, an optional initial value and a collection:

Again, you can use any function that returns true or false as the first argument to filter . Here is an example that keeps only words less than five characters long. (The count function returns the length of its argument.)

For filtering the values of a data structure we use the filter function, which takes a predicate and a sequence and gives a new sequence with only the elements that returned true for the given predicate:

If your function is short, you can use an anonymous function instead, either the normal or short syntax:

The first parameter for map can be any function that takes one argument and returns a value. For example, if you had a graphing application and you wanted to graph the equation y = 3x + 5 for a set of x values, you could get the y values like this:

For transforming every value in an iterable data structure we use the map function, which takes a function and a sequence and applies the function to every element:

The above actions are encoded in higher-order functions and syntactic constructs in ClojureScript; let’s see an example of the first three.

Convert the iterable to a value where each iteration depends on the result from the previous one

In imperative programming languages it is common to use for loops to iterate over data and transform it, usually with the intent being one of the following:

Note that loop isn’t the only point we can recur to; using recur inside a function executes the body of the function recursively with the new bindings:

In the above snippet, we bind the name x to the value 0 and execute the body. Since the condition is not met the first time, it’s rerun with recur , incrementing the binding value with the inc function. We do this once more until the condition is met and, since there aren’t any more recur calls, exit the loop.

Let’s take a look at how to express loops using recursion with the loop and recur forms. loop defines a possibly empty list of bindings (notice the symmetry with let ) and recur jumps execution back to the looping point with new values for those bindings.

Many of the common patterns for which for is used in other languages are achieved through higher-order functions - functions that accept other functions as parameters.

The functional approach of ClojureScript means that it does not have standard, well-known, statement-based loops such as for in JavaScript. The loops in ClojureScript are handled using recursion. Recursion sometimes requires additional thinking about how to model your problem in a slightly different way than imperative languages.

The body of the let expression, explained in the previous section, is very similar to the do expression in that it allows multiple expressions. In fact, the let has an implicit do .

;; this value will not be returned; it is thrown away

The do expression accepts as its parameter an arbitrary number of other expressions, but it returns the return value only from the last one:

A side effect is something that is not necessary for the return value.

In JavaScript, braces { and } delimit a block of code that “belongs together”. Blocks in ClojureScript are created using the do expression and are usually used for side effects, like printing something to the console or writing a log in a logger.

In the preceding example, the symbol x is bound to the value (inc 1) , which comes out to 2, and the symbol y is bound to the sum of x and 1, which comes out to 3. Given those bindings, the expressions (println "Simple message from the body of a let") and (* x y) are evaluated.

Locals are defined with the let expression. The expression starts with a vector as the first parameter followed by an arbitrary number of expressions. The first parameter (the vector) should contain an arbitrary number of pairs that give a binding form (usually a symbol) followed by an expression whose value will be bound to this new local for the remainder of the let expression.

ClojureScript does not have the concept of variables as in ALGOL-like languages, but it does have locals. Locals, as per usual, are immutable, and if you try to mutate them, the compiler will throw an error.

Queues are not as frequently used as lists or vectors, but it is good to know that they are available in ClojureScript, as they may occasionally come in handy.

A thing to bear in mind about queues is that the stack operations don’t follow the usual stack semantics (pushing and popping from the same end). pop takes values from the front position, and conj pushes (appends) elements to the back.

ClojureScript also provides a persistent and immutable queue. Queues are not used as pervasively as other collection types. They can be created using the #queue [] literal syntax, but there are no convenient constructor functions for them.

Sets have a sorted counterpart like maps do that are created using the functions sorted-set and sorted-set-by which are analogous to map’s sorted-map and sorted-map-by .

Sets act as read-only associative data that associates the values it contains to themselves. Since every value except nil and false is truthy in ClojureScript, we can use sets as predicate functions:

Sets also support the generic conj operation just like every other collection does.

A nice property of immutable sets is that they can be nested. Languages that have mutable sets can end up containing duplicate values, but that can’t happen in ClojureScript. In fact, all ClojureScript data structures can be nested arbitrarily due to immutability.

There are many operations that can be performed with sets, although they are located in the clojure.set namespace and thus need to be imported. You’ll learn the details of namespacing later; for now, you only need to know that we are loading a namespace called clojure.set and binding it to the s symbol.

Set literals cannot contain duplicate values. If you accidentally write a set literal with duplicates an error will be thrown:

Sets in ClojureScript have literal syntax as values enclosed in #{} and they can be created with the set constructor. They are unordered collections of values without duplicates.

If we need a custom ordering we can provide a comparator function to sorted-map-by , let’s see an example inverting the value returned by the built-in compare function. Comparator functions take two items to compare and return -1 (if the first item is less than the second), 0 (if they are equal), or 1 (if the first item is greater than the second).

ClojureScript also offers sorted hash maps which behave like their unsorted versions but preserve order when iterating over them. We can create a sorted map with default ordering with sorted-map :

Maps are also functions of their keys, returning the values related to the given keys. Unlike vectors, they return nil if we supply a key that is not present in the map:

Maps associate keys to values and, as such, are an associative data structure. They support adding associations with assoc and, unlike vectors, removing them with dissoc . assoc will also update the value of an existing key. Let’s explore these functions:

In the preceding example, it just so happens that the order was preserved, but if you have many keys, you will see that the order is not preserved.

Since regular maps don’t have a specific order, the conj operation just adds one or more key-value pairs to a map. conj for maps expects one or more sequences of key-value pairs as its last arguments:

ClojureScript maps are written literally as key-value pairs enclosed in braces {} . Alternatively, they can be created with the hash-map function:

Maps are ubiquitous in ClojureScript. Like vectors, they are also used as a syntactic construct, particularly for attaching metadata to vars. Any ClojureScript data structure can be used as a key in a map, although it’s common to use keywords since they can also be called as functions.

The map and filter operations return lazy sequences, but as it is common to need a fully realized sequence after performing those operations, vector-returning counterparts of such functions are available as mapv and filterv . They have the advantages of being faster than building a vector from a lazy sequence and making your intent more explicit:

As with lists, vectors can also be used as stacks with the peek , pop , and conj functions. Note, however, that vectors grow from the opposite end of the collection as lists:

Perhaps surprisingly, associative data structures can also be used as functions. They are functions of their keys to the values they are associated with. In the case of vectors, if the given key is not present an exception is thrown:

Note that we can only assoc to a key that is either contained in the vector already or if it is the last position in a vector:

Since vectors associate sequential numeric keys (indexes) to values, we can treat them as an associative data structure. ClojureScript provides the assoc function that, given an associative data structure and a set of key-value pairs, yields a new data structure with the values corresponding to the keys modified. Indexes begin at zero for the first element in a vector.

Another thing that differentiates lists and vectors is that vectors are indexed collections and as such support efficient random index access and non-destructive updates. We can use the nth function to retrieve values given an index:

Vectors are, like lists, ordered collections of heterogeneous values. Unlike lists, vectors grow naturally from the tail, so the conj operation appends items to the end of a vector. Insertion on the end of a vector is effectively constant time:

ClojureScript vectors have enclosing brackets [] in their syntax literals. They can be created with vector and from another collection with vec :

Vectors are one of the most common data structures in ClojureScript. They are used as a syntactic construct in many places where more traditional Lisps use lists, for example in function argument declarations and let bindings.

One thing that lists are not particularly good at is random indexed access. Since they are stored in a single linked list-like structure in memory, random access to a given index requires a linear traversal in order to either retrieve the requested item or throw an index out of bounds error. Non-indexed ordered collections like lazy sequences also suffer from this limitation.

Note that the two operations that return a stack ( conj and pop ) don’t change the type of the collection used for the stack.

Lists and other ClojureScript data structures can be used as stacks using the peek , pop , and conj functions. Note that the top of the stack will be the "place" where conj adds elements, making conj equivalent to the stack’s push operation. In the case of lists, conj adds elements to the front of the list, peek returns the first element of the list, and pop returns a list with all the elements but the first one.

Since the head is the position that has constant time addition in the list collection, the conj operation on lists naturally adds items to the front:

We used the literal () to represent the empty list. Since it doesn’t contain any symbols, it is not treated as a function call. However, when using list literals that contain elements, we need to quote them to prevent ClojureScript from evaluating them as a function call:

You can think of ClojureScript lists as singly linked lists, where each node contains a value and a pointer to the rest of the list. This makes it natural (and fast!) to add items to the front of the list, since adding to the end would require traversal of the entire list. The prepend operation is performed using the cons function.

In ClojureScript, lists are mostly used as a data structure for grouping symbols together into programs. Unlike in other Lisps, many of the syntactic constructs of ClojureScript use data structures different from the list (vectors and maps). This makes code less uniform, but the gains in readability are well worth the price.

Now that we’re acquainted with ClojureScript’s sequence abstraction and some of the generic sequence manipulating functions, it’s time to dive into the concrete collection types and the operations they support.

Here is a contrived example. Let’s say you are writing a graphing program and you are graphing the equation y= 2 x 2 + 5, and you want only those values of x for which the y value is less than 100. You can generate all the numbers 0 through 100, which will certainly be enough, and then take-while the condition holds:

If you just say (range) , you will get an infinite sequence of all the integers. Do not try this in the REPL, unless you are prepared to wait for a very, very long time, because the REPL wants to fully evaluate the expression.

Most of ClojureScript’s sequence-returning functions generate lazy sequences instead of eagerly creating a whole new sequence. Lazy sequences generate their contents as they are requested, usually when iterating over them. Laziness ensures that we don’t do more work than we need to and gives us the possibility of treating potentially infinite sequences as regular ones.

We can pass as many elements as we want to add to conj ; let’s see it in action:

The conj operation adds elements to collections and may add them in different "places" depending on the type of collection. It adds them where it is most performant for the collection type, but note that not every collection has a defined order.

We can also get an empty variant of a given collection with the empty function:

For collections that can be counted in constant time, we can use the count operation. This operation also works on strings, even though, as you have seen, they are not collections, sequences, or seqable.

Similar predicates exist for checking if a value is a sequence (with seq? ) or a seqable (with seqable? ):

We can query a value to know whether it’s a collection type with the coll? predicate:

We already saw examples with the usual suspects like map , filter , and reduce , but ClojureScript offers a plethora of generic sequence operations in its core namespace. Note that many of the operations we’ll learn about either work with seqables or are extensible to user-defined types.

As you may have noticed, functions that operate on sequences are safe to use with empty collections or even nil values since they don’t need to do anything but return an empty sequence when encountering such values.

Obviously the same operation can be done in more idiomatic way only obtaining a seq of values:

The ClojureScript core functions for transforming collections make sequences out of their arguments and are implemented in terms of the generic sequence operations we learned about in the preceding section. This makes them highly generic because we can use them on any data type that is seqable. Let’s see how we can use map with a variety of seqables:

Though nil is neither a seqable nor a sequence, it is supported by all the functions we saw so far:

Since seq returns nil when the collection is empty, and nil evaluates to false in boolean context, you can check to see if a collection is empty by using the seq function. The technical term for this is nil-punning.

next is a similar sequence operation to rest , but it differs from the latter in that it yields a nil value when called with a sequence with one or zero elements. Note that, when given one of the aforementioned sequences, the empty sequence returned by rest will evaluate as a boolean true whereas the nil value returned by next will evaluate as false ( see the section on truthiness later in this chapter ).

Calling seq on a seqable can yield different results if the seqable is empty or not. It will return nil when empty and a sequence otherwise:

The types that can be used to generate a sequence are called "seqables"; we can call seq on them and get a sequence back. Sequences support two basic operations: first and rest . They both call seq on the argument we provide them:

One of the central ClojureScript abstractions is the sequence which can be thought of as a list and can be derived from any of the collection types. It is persistent and immutable like all collection types, and many of the core ClojureScript functions return sequences.

As you can see in the example, we used cons (construct) to prepend a value to the xs list and we got a new list ys with the element added. The rest of the ys list (all the values but the first) are the same object in memory as the xs list, thus xs and ys share structure.

For illustrating the structural sharing of ClojureScript data structures, let’s compare whether some parts of the old and new versions of a data structure are actually the same object with the identical? predicate. We’ll use the list data type for this purpose:

If you want to see an example of how structural sharing works, read on. If you’re not interested in more details you can skip over to the next section .

A persistent data structure is a data structure that returns a new version of itself when transforming it, leaving the original unmodified. ClojureScript makes this memory and time efficient using an implementation technique called structural sharing, where most of the data shared between two versions of a value is not duplicated and transformations of a value are implemented by copying the minimal amount of data required.

As you can see, we derived a new version of the xs vector appending an element to it and got a new vector ys with the element added. However, the xs vector remained unchanged because it is immutable.

Let’s illustrate that with an example: appending values to a vector using the conj (conjoin) operation.

An immutable data structure, as its name suggests, is a data structure that cannot be changed. In-place updates are not allowed in immutable data structures.

We mentioned before that ClojureScript collections are persistent and immutable, but we didn’t explain what that meant.

Destructuring, as its name suggests, is a way of taking apart structured data such as collections and focusing on individual parts of them. ClojureScript offers a concise syntax for destructuring both indexed sequences and associative data structures that can be used any place where bindings are declared.

Let’s see an example of what destructuring is useful for that will help us understand the previous statements better. Imagine that you have a sequence but are only interested in the first and third item. You could get a reference to them easily with the nth function:

( let [ v [ 0 1 2 ] fst ( nth v 0 ) thrd ( nth v 2 )] [ thrd fst ]) ;; => [2 0]

However, the previous code is overly verbose. Destructuring lets us extract values of indexed sequences more succintly using a vector on the left-hand side of a binding:

( let [[ fst _ thrd ] [ 0 1 2 ]] [ thrd fst ]) ;; => [2 0]

In the above example, [fst _ thrd] is a destructuring form. It is represented as a vector and used for binding indexed values to the symbols fst and thrd , corresponding to the index 0 and 2 , respectively. The _ symbol is used as a placeholder for indexes we are not interested in — in this case 1 .

Note that destructuring is not limited to the let binding form; it works in almost every place where we bind values to symbols such as in the for and doseq special forms or in function arguments. We can write a function that takes a pair and swaps its positions very concisely using destructuring syntax in function arguments:

( defn swap-pair [[ fst snd ]] [ snd fst ]) ( swap-pair [ 1 2 ]) ;; => [2 1] ( swap-pair ' ( 3 4 )) ;; => [4 3]

Positional destructuring with vectors is quite handy for taking indexed values out of sequences, but sometimes we don’t want to discard the rest of the elements in the sequence when destructuring. Similarly to how & is used for accepting variadic function arguments, the ampersand can be used inside a vector destructuring form for grouping together the rest of a sequence:

( let [[ fst snd & more ] ( range 10 )] { :first fst :snd snd :rest more }) ;; => {:first 0, :snd 1, :rest (2 3 4 5 6 7 8 9)}

Notice how the value in the 0 index got bound to fst , the value in the 1 index got bound to snd , and the sequence of elements from 2 onwards got bound to the more symbol.

We may still be interested in a data structure as a whole even when we are destructuring it. This can be achieved with the :as keyword. If used inside a destructuring form, the original data structure is bound to the symbol following that keyword:

( let [[ fst snd & more :as original ] ( range 10 )] { :first fst :snd snd :rest more :original original }) ;; => {:first 0, :snd 1, :rest (2 3 4 5 6 7 8 9), :original (0 1 2 3 4 5 6 7 8 9)}

Not only can indexed sequences be destructured, but associative data can also be destructured. Its destructuring binding form is represented as a map instead of a vector, where the keys are the symbols we want to bind values to and the values are the keys that we want to look up in the associative data structure. Let’s see an example:

( let [{ language :language } { :language "ClojureScript" }] language ) ;; => "ClojureScript"

In the above example, we are extracting the value associated with the :language key and binding it to the language symbol. When looking up keys that are not present, the symbol will get bound to nil :

( let [{ name :name } { :language "ClojureScript" }] name ) ;; => nil

Associative destructuring lets us give default values to bindings which will be used if the key isn’t found in the data structure we are taking apart. A map following the :or keyword is used for default values as the following examples show:

( let [{ name :name :or { name "Anonymous" }} { :language "ClojureScript" }] name ) ;; => "Anonymous" ( let [{ name :name :or { name "Anonymous" }} { :name "Cirilla" }] name ) ;; => "Cirilla"

Associative destructuring also supports binding the original data structure to a symbol placed after the :as keyword:

( let [{ name :name :as person } { :name "Cirilla" :age 49 }] [ name person ]) ;; => ["Cirilla" {:name "Cirilla" :age 49}]

Keywords aren’t the only things that can be the keys of associative data structures. Numbers, strings, symbols and many other data structures can be used as keys, so we can destructure using those, too. Note that we need to quote the symbols to prevent them from being resolved as a var lookup:

( let [{ one 1 } { 0 "zero" 1 "one" }] one ) ;; => "one" ( let [{ name "name" } { "name" "Cirilla" }] name ) ;; => "Cirilla" ( let [{ lang 'language } { 'language "ClojureScript" }] lang ) ;; => "ClojureScript"

Since the values corresponding to keys are usually bound to their equivalent symbol representation (for example, when binding the value of :language to the symbol language ) and keys are usually keywords, strings, or symbols, ClojureScript offers shorthand syntax for these cases.

We’ll show examples of all of these, starting with destructuring keywords using :keys :

( let [{ :keys [ name surname ]} { :name "Cirilla" :surname "Fiona" }] [ name surname ]) ;; => ["Cirilla" "Fiona"]

As you can see in the example, if we use the :keys keyword and associate it with a vector of symbols in a binding form, the values corresponding to the keywordized version of the symbols will be bound to them. The {:keys [name surname]} destructuring is equivalent to {name :name surname :surname} , only shorter.

The string and symbol shorthand syntax works exactly like :keys , but using the :strs and :syms keywords respectively:

( let [{ :strs [ name surname ]} { "name" "Cirilla" "surname" "Fiona" }] [ name surname ]) ;; => ["Cirilla" "Fiona"] ( let [{ :syms [ name surname ]} { 'name "Cirilla" 'surname "Fiona" }] [ name surname ]) ;; => ["Cirilla" "Fiona"]

If the map you want to destructure has namespaced keywords as keys, you also can do it using the keyword syntax inside :keys vector:

( let [{ :keys [ ::name ::surname ]} { ::name "Cirilla" ::surname "Fiona" }] [ name surname ]) ;; => ["Cirilla" "Fiona"]

An interesting property of destructuring is that we can nest destructuring forms arbitrarily, which makes code that accesses nested data on a collection very easy to understand, as it mimics the collection’s structure: