Clojure programs are written in expressions which are evaluated to results. If an expression needs to be compiled, it will be. The compiler produces JVM bytecode. Programs can be loaded from files or evaluated dynamically in a Read Eval Print Loop (REPL). This chapter is an overview of the language.
Anything following semi-colon is a comment
; this is the preferred way to write a comment
Less common is the comment form:
(comment anything)
And a special form for complete removal of any form it is prefixed to
#_(this form is removed)
which is handy for temporarily removing a form when modifying code.
Strings are enclosed in double quotes and may span multiple lines.
"this is a string"
Character literals are preceded by a backslash
\a \b \c
Numbers include valid Java numbers, BigInteger, BigDecimal and ratios.
1; the integer one
3.14; a Double
1000000000000000000000N; a BigInteger
1e100M; a BigDecimal
2/5; the ratio two fifths
Numbers are automatically promoted to Big if they overflow during arithmetic.
Booleans are represented as true and false.
nil means nothing and is false in logical tests.
Lists are zero or more forms enclosed in parentheses:
()
Lists are evaluated as function calls
(inc 1)
=> 2
Quote yields the unevaluated form.
(quote (1 2))
=> (1 2)
Vectors are enclosed in square braces
[1 2 3 4]
Vectors are used in preference to lists for cases where either could be used, as vectors do not require quoting and are visually distinct.
Clojure reports sequences and lists as visually the same
(seq [1 2 3])
=> (1 2 3)
And tests for equivalence of sequences
(= [1 2 3] (quote (1 2 3)))
=> true
Hashmaps are key/value pairs
{"Language" "Clojure",
"Version" 1.5,
"Author" "Rich Hickey"}
Commas are optional and treated as whitespace.
Keywords are an optimization for string keys
{:language "Clojure", :version 1.5, :author "Rich Hickey"}
:language :version :author
They must begin with a colon, and cannot contain a period or name classes. Double colon resolves the keyword in the current namespace. In the x namespace, ::rect is :x/rect
Sets are written as
#{1 2 3}
Persistent reference to a changing value are called vars.
(def myvar 1)
myvar
=> 1
Symbols begin with a non-numeric character and can contain alphanumeric characters. Symbols are used for naming things and must be either defined or bound before they can be used. In the snippet above def creates a var named myvar which is bound to the value 1.
A var is rarely rebound to a new value. To represent data which changes over time, create a var which is bound to a ref, agent, or atom. These are mechanisms for controlling change. To get the value of a ref, agent or atom you deref the var bound to it using @
(def a (atom 1))
(swap! a inc)
@a
=> 2
Scope and labels are created with the let form which binds symbols to values
(let [x 1]
(inc x))
=> 2
The symbol x is bound to the value 1, and then the function inc is called on x.
It is preferable to use let instead of def where either could be used.
Unnamed functions known as Lambda expressions are written as
(fn [a] (inc a))
There is a special syntax for creating anonymous functions
#(inc %)
Returns a function which increments a single argument
#(= 1 %1 %2)
Returns a function which compares 1 to two other arguments
The result of the last expression in a function is returned as the result of the function. All functions return a result. The result can be nil if the function was called to produce a side effect.
Functions are defined like this:
(defn square [x]
(* x x))
All expressions implicitly return a result. The code above defines a function square which returns the result of multiplying the input parameter x by itself.
Mathematical operators are regular functions which must be written in prefix notation.
Arguments are evaluated from left to right.
(+ (square 2) (square 3))
=> 13
The first item in a list can also be a special form (built-in primitive) or a macro, both of which can manipulate the operand form instead of evaluating them as input arguments.
defn is a macro to def a var square which is bound to a function. Macros provide syntactic sugar. The form is expanded at compile time through manipulation of the form. You can see the expansion using macroexpand
(macroexpand-1 ‘(defn square [x] (* x x)))
=> (def edge.examples-test/square (clojure.core/fn ([edge.examples-test/x] (clojure.core/* edge.examples-test/x edge.examples-test/x))))
macroexpand-1 limits the expansion to a depth of one. During expansion the namespace of the symbols is explicitly shown.
Special Forms are similar to macros but are handled by the reader.
To help write macros there are two ways to avoid evaluation. The first is to quote the form. You can use apostrophe as shorthand for quote.
'(1 2 3)
=> (1 2 3)
Syntax-quote (`, note, the "backquote" character), Unquote (~) and Unquote-splicing (~@)
#^
`
~
~@
‘(1 2)
=> (1 2)
Macros will not be covered in detail in this book, it is enough to know the difference between macros and functions; macro arguments are manipulated at compile time instead of evaluated.
(if false
1
2)
=> 2
Note that if is not a function, as it will only evaluate the branch indicated by the boolean test.
Switch statements are written using cond and condp:
(cond
false 1
true 2)
=> 2
(condp #(= % 2)
1 “apple”
2 “pear”)
=> “pear”
and or
Constructing a Java object is done by appending a trailing period
(java.util/Date.)
Calling a method on a Java object done by prepending a leading period
(.isDirectory (file s))
Java static method calls are accessed by slash
(Math/pow 1 2)
deftype, defrecord, and reify provide the mechanism for defining implementations of abstractions, and instances of those implementations. Resist the urge to use them to define ‘structured data’ as you would define classes or structures in other languages. It is preferred to use the built-in datatypes (vectors, maps, sets) to represent structured data.
(defn normalize
"Divide all dimensions by the sum of squares"
[[x y]]
(let [length (Math/sqrt (+ (* x x) (* y y)))]
[(/ x length) (/ y length)]))
Note that function arguments are already a destructured vectors. The above case is an example of a vector of arguments which contains a vector of x and y.
Destructuring avoids us having to extract substructure manually:
;; bad: x and y must be extracted
(defn normalize
"Divide all dimensions by the sum of squares"
[v]
(let [x (first v)
y (second v)
length (Math/sqrt (+ (* x x) (* y y)))]
[(/ x length) (/ y length)]))
Destructuring is very useful in list comprehension with for:
(defn invert [m]
(into {} (for [[k v] m]
[v k])))
for is largely equivalent to map, however has a convenient syntax and allows :let :where :while additional constraints to be expressed.
Notice however that there is no need to restrict normalize to use 2 dimensions, instead we can write a generic version:
(defn normalize
"Divide all dimensions by the sum of squares"
[dims]
(let [squares (map * dims dims)
length (Math/sqrt (reduce + squares))
by-length #(/ % length)]
(map by-length dims)))
(normalize [3 4]) -> (0.6 0.8)
(normalize [3 4 5]) -> …
Vardic functions are destructured using &
(defn sub [& vs]
(apply map - vs))
Which produces a vector.
Apply expands the vector arguments.
Most mathematical functions are vardic:
(+ 1 2 3)
=> 6
Destructuring is nested, so you can use it to pull out sub-values without resorting to getter functions.
Regular expressions are written as #”pattern”
(re-seq #"\w+" "the quick brown fox")
=> ("the" "quick" "brown" "fox")
Metadata may be provided using ^{}
(def x ^{:private true} 1)
You can attach whatever metadata you wish, these are the keys the compiler looks for:
:private
:doc
:author
:type
There has been error in communication with Booktype server. Not sure right now where is the problem.
You should refresh this page.