Clojure Tutorial
Clojure Tutorial
This tutorial is fairly comprehensive and covers various functions involved in Clojure. All
the functions are explained using examples for easy understanding.
Audience
This tutorial is designed for all those software professionals who are keen on learning the
basics of Clojure and how to put it into practice.
Prerequisites
Before proceeding with this tutorial, familiarity with Java and LISP programming language
is preferred.
All the content and graphics published in this e-book are the property of Tutorials Point (I)
Pvt. Ltd. The user of this e-book is prohibited to reuse, retain, copy, distribute or republish
any contents or a part of contents of this e-book in any manner without written consent
of the publisher.
We strive to update the contents of our website and tutorials as timely and as precisely as
possible, however, the contents may contain inaccuracies or errors. Tutorials Point (I) Pvt.
Ltd. provides no guarantee regarding the accuracy, timeliness or completeness of our
website or its contents including this tutorial. If you discover any errors on our website or
in this tutorial, please notify us at contact@[Link]
i
Clojure
Table of Contents
About the Tutorial .................................................................................................................................... i
Audience .................................................................................................................................................. i
Prerequisites ............................................................................................................................................ i
Namespaces .......................................................................................................................................... 14
Delimiters ............................................................................................................................................. 16
Whitespaces .......................................................................................................................................... 17
Symbols................................................................................................................................................. 17
ii
Clojure
8. CLOJURE - LOOPS.............................................................................................................. 37
Loop Statement..................................................................................................................................... 41
If Statement .......................................................................................................................................... 44
iii
Clojure
list* ....................................................................................................................................................... 76
first ....................................................................................................................................................... 77
nth ........................................................................................................................................................ 78
cons....................................................................................................................................................... 78
conj ....................................................................................................................................................... 79
rest........................................................................................................................................................ 80
iv
Clojure
sorted-set .............................................................................................................................................. 81
get ......................................................................................................................................................... 82
contains? ............................................................................................................................................... 82
conj ....................................................................................................................................................... 83
disj ........................................................................................................................................................ 84
union ..................................................................................................................................................... 84
difference .............................................................................................................................................. 85
intersection ........................................................................................................................................... 85
subset? .................................................................................................................................................. 86
superset? .............................................................................................................................................. 87
vector-of ............................................................................................................................................... 88
nth ........................................................................................................................................................ 89
get ......................................................................................................................................................... 90
conj ....................................................................................................................................................... 90
pop........................................................................................................................................................ 91
subvec ................................................................................................................................................... 92
get ......................................................................................................................................................... 94
contains? ............................................................................................................................................... 95
find........................................................................................................................................................ 95
keys ....................................................................................................................................................... 96
vals ........................................................................................................................................................ 97
v
Clojure
dissoc .................................................................................................................................................... 97
merge .................................................................................................................................................... 98
merge-with ........................................................................................................................................... 99
select-keys ............................................................................................................................................ 99
ns ........................................................................................................................................................ 103
cons..................................................................................................................................................... 116
vii
Clojure
compare-and-set!................................................................................................................................ 140
meta.................................................................................................................................................... 144
vary-meta............................................................................................................................................ 144
struct................................................................................................................................................... 146
viii
Clojure
ix
Clojure
Web Applications – Adding More Routes to Your Web Application .................................................... 190
x
1. Clojure - Overview Clojure
Before we talk about Clojure, let’s just have a quick description of LISP programming
language. LISPs have a tiny language core, almost no syntax, and a powerful macro
facility. With these features, you can bend LISP to meet your design, instead of the other
way around. LISP has been there for a long time dating back to 1958.
Common LISP reads in an expression, evaluates it, and then prints out the result. For
example, if you want to compute the value of a simple mathematical expression of 4+6
then you type in
USER(1) (+ 4 6)
It is based on the LISP programming language which makes its code statements
smaller than traditional programming languages.
It focuses on immutability which is basically the concept that you should not make
any changes to objects which are created in place.
It supports concurrency.
It embraces existing programming languages. For example, Clojure can make use
of the entire Java ecosystem for management of the running of the code via the
JVM.
1
2. Clojure – Environment Clojure
There are a variety of ways to work with Clojure as a programming language. We will look
at two ways to work with Clojure programming.
Leiningen Installation
Ensure the following System requirements are met before proceeding with the installation.
System Requirements
JDK JDK 1.7 or above
2
Clojure
3
Clojure
Step 3: Specify the location for the installation and click the Next button.
4
Clojure
Step 4: The setup will detect the location of an existing Java installation. Click the Next
button to proceed.
5
Clojure
6
Clojure
After the installation is complete, it will give you the option to open a Clojure REPL, which
is an environment that can be used to create and test your Clojure programs.
Eclipse Installation
Ensure the following System requirements are met before proceeding with the installation.
System Requirements
JDK JDK 1.7 or above
7
Clojure
Step 1: Open Eclipse and click the Menu item. Click Help -> Eclipse Marketplace.
8
Clojure
Step 2: Type in the keyword Clojure in the dialog box which appears and hit the ‘Go’
button. The option for counterclockwise will appear, click the Install button to begin the
installation of this plugin.
9
Clojure
Step 3: In the next dialog box, click the Confirm button to begin the installation.
10
Clojure
Step 4: In the next dialog box, you will be requested to accept the license agreement.
Accept the license agreement and click the Finish button to continue with the installation.
The installation will begin, and once completed, it will prompt you to restart Eclipse.
11
Clojure
Once Eclipse is restarted, you will see the option in Eclipse to create a new Clojure project.
12
3. Clojure - Basic Syntax Clojure
In order to understand the basic syntax of Clojure, let’s first look at a simple Hello World
program.
(ns [Link]
(:gen-class))
(defn hello-world []
(println "Hello World"))
(hello-world)
The program will be written in a file called [Link]. The extension ‘clj’ is the
extension name for a clojure code file. In the above example, the name of the file
is called [Link].
The ‘defn’ keyword is used to define a function. We will see functions in details in
another chapter. But for now, know that we are creating a function called hello-
world, which will have our main Clojure code.
In our Clojure code, we are using the ‘println’ statement to print “Hello World” to
the console output.
We then call the hello-world function which in turn runs the ‘println’ statement.
Hello World
(+ 1 2)
13
Clojure
In the above example, the entire expression is enclosed in braces. The output of the above
statement is 3. The + operator acts like a function in Clojure, which is used for the addition
of numerals. The values of 1 and 2 are known as parameters to the function.
Let us consider another example. In this example, ‘str’ is the operator which is used to
concatenate two strings. The strings “Hello” and “World” are used as parameters.
If we combine the above two statements and write a program, it will look like the following.
(ns [Link]
(:gen-class))
(defn Example []
(println (str "Hello World"))
(println (+ 1 2)))
(Example)
Hello World
3
Namespaces
A namespace is used to define a logical boundary between modules defined in Clojure.
Current Namespace
This defines the current namespace in which the current Clojure code resides in.
Syntax
*ns*
Example
In the REPL command window run the following command.
*ns*
14
Clojure
Output
When we run the above command, the output will defer depending on what is the current
namespace. Following is an example of an output. The namespace of the Clojure code is:
[Link]
(ns [Link]
(:gen-class))
(defn Example []
(println (str "Hello World"))
(println (+ 1 2)))
(Example)
Syntax
(require quoted-namespace-symbol)
(ns [Link]
(:gen-class))
(require ‘[Link]’)
(defn Example []
(.exists (file "[Link]"))
(Example)
In the above code, we are using the ‘require’ keyword to import the namespace
[Link] which has all the functions required for input/output functionality. Since we
not have the required library, we can use the ‘file’ function in the above code.
15
Clojure
Comments in Clojure
Comments are used to document your code. Single line comments are identified by using
the ;; at any position in the line. Following is an example.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(println "Hello World"))
(Example)
Delimiters
In Clojure, statements can be split or delimited by using either the curved or square
bracket braces.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(println (+ 1 2
3))
)
(Example)
16
Clojure
Whitespaces
Whitespaces can be used in Clojure to split different components of a statement for better
clarity. This can be done with the assistance of the comma (,) operator.
For example, the following two statements are equivalent and the output of both the
statements will be 15.
(+ 1 2 3 4 5)
(+ 1, 2, 3, 4, 5)
Although Clojure ignores commas, it sometimes uses them to make things easier for the
programmer to read.
For instance, if you have a hash map like the following (def a-map {:a 1 :b 2 :c 3}) and
ask for its value in the REPL window, Clojure will print the output as {:a 1, :b 2, :c 3}.
The results are easier to read, especially if you’re looking at a large amount of data.
Symbols
In Clojure, symbols are equivalent to identifiers in other programming languages. But
unlike other programming languages, the compiler sees symbols as actual string values.
As a symbol is a value, a symbol can be stored in a collection, passed as an argument to
a function, etc., just like any other object.
A symbol can only contain alphanumeric characters and ‘* + ! / . : - _ ?’ but must not
begin with a numeral or colon.
tutorial-point!
TUTORIAL
+tutorial+
17
Clojure
18
Clojure
Following key things need to be noted about the above program structure.
demo_1 – This is the package in which the Clojure code file is placed.
[Link] – This is the main Clojure code file, which will contain the code for the
Clojure application.
The Leiningen folder contains files like [Link] which is required to run
any Clojure-based application.
The [Link] file will contain information such as the groupId, artifactId and
version of the Clojure project.
The [Link] file contains information about the Clojure application itself.
Following is a sample of the project file contents.
19
4. Clojure - REPL Clojure
REPL (read-eval-print loop) is a tool for experimenting with Clojure code. It allows you to
interact with a running program and quickly try out if things work out as they should. It
does this by presenting you with a prompt where you can enter the code. It then reads
your input, evaluates it, prints the result, and loops, presenting you with a prompt again.
This process enables a quick feedback cycle that isn’t possible in most other languages.
lein repl
You then start evaluating Clojure commands in the REPL window as required.
20
Clojure
To start a REPL session in Eclipse, click the Menu option, go to Run As -> Clojure
Application.
21
Clojure
This will start a new REPL session in a separate window along with the console output.
Conceptually, REPL is similar to Secure Shell (SSH). In the same way that you can use
SSH to interact with a remote server, Clojure REPL allows you to interact with a running
Clojure process. This feature can be very powerful because you can even attach a REPL to
a live production app and modify your program as it runs.
22
Clojure
In the above example, first two strings are being sent to the REPL output window as “Hello”
and “World” respectively. Then the *2 and *1 variables are used to recall the last 2
evaluated expressions.
23
5. Clojure - Data Types Clojure
o Decimal Integers (Short, Long and Int) – These are used to represent whole
numbers. For example, 1234.
Floating point.
o The default is used to represent 32-bit floating point numbers. For example,
12.34.
char – This defines a single character literal. Characters are defined with the
backlash symbol. For example, /e.
Boolean – This represents a Boolean value, which can either be true or false.
String – These are text literals which are represented in the form of chain of
characters. For example, “Hello World”.
24
Clojure
Bound Values
Since all of the datatypes in Clojure are inherited from Java, the bounded values are the
same as in Java programming language. The following table shows the maximum allowed
values for the numerical and decimal literals.
Name
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
The following program shows a consolidated clojure code to demonstrate the data types
in Clojure.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
;; The below code declares a integer variable
(def x 1)
;; The below code declares a float variable
(def y 1.25)
;; The below code declares a string variable
(def str1 "Hello")
(println x)
25
Clojure
(println y)
(println str1)
)
(Example)
1
1.25
Hello
26
6. Clojure - Variables Clojure
In Clojure, variables are defined by the ‘def’ keyword. It’s a bit different wherein the
concept of variables has more to do with binding. In Clojure, a value is bound to a variable.
One key thing to note in Clojure is that variables are immutable, which means that in order
for the value of the variable to change, it needs to be destroyed and recreated again.
float – This is used to represent 32-bit floating point numbers. For example, 12.34.
Boolean – This represents a Boolean value, which can either be true or false.
String – These are text literals which are represented in the form of chain of
characters. For example, “Hello World”.
Variable Declarations
Following is the general syntax of defining a variable.
Where ‘var-name’ is the name of the variable and ‘var-value’ is the value bound to the
variable.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
;; The below code declares a integer variable
(def x 1)
;; The below code declares a float variable
(def y 1.25)
;; The below code declares a string variable
(def str1 "Hello")
;; The below code declares a boolean variable
27
Clojure
Naming Variables
The name of a variable can be composed of letters, digits, and the underscore character.
It must begin with either a letter or an underscore. Upper and lowercase letters are distinct
because Clojure, just like Java is a case-sensitive programming language.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
;; The below code declares a Boolean variable with the name of status
(def status true)
;; The below code declares a Boolean variable with the name of STATUS
(def STATUS false)
;; The below code declares a variable with an underscore character.
(def _num1 2)
)
(Example)
Note: In the above statements, because of the case sensitivity, status and STATUS are
two different variable defines in Clojure.
The above example shows how to define a variable with an underscore character.
Printing variables
Since Clojure uses the JVM environment, you can also use the ‘println’ function. The
following example shows how this can be achieved.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
;; The below code declares a integer variable
28
Clojure
(def x 1)
;; The below code declares a float variable
(def y 1.25)
;; The below code declares a string variable
(def str1 "Hello")
(println x)
(println y)
(println str1)
)
(Example)
1
1.25
Hello
29
7. Clojure - Operators Clojure
An operator is a symbol that tells the compiler to perform specific mathematical or logical
manipulations.
Arithmetic operators
Relational operators
Logical operators
Bitwise operators
Note: In Clojure, operators and operands work in the following syntax manner.
For example,
(+ 1 2)
Arithmetic Operators
Clojure language supports the normal Arithmetic operators as any language. Following are
the Arithmetic operators available in Clojure.
30
Clojure
The following code snippet shows how the various operators can be used.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def x (+ 2 2))
(println x)
(def x (- 2 1))
(println x)
(def x (* 2 2))
(println x)
(def x (float(/ 2 1)))
(println x)
(def x (inc 2))
(println x)
(def x (dec 2))
(println x)
(def x (max 1 2 3))
(println x)
(def x (min 1 2 3))
(println x)
31
Clojure
4
1
4
2.0
3
1
3
1
1
Relational Operators
Relational operators allow comparison of objects. Following are the relational operators
available in Clojure.
not= Tests the difference between two objects (not= 3 2) will give true
32
Clojure
The following code snippet shows how the various operators can be used.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def x (= 2 2))
(println x)
(def x (not= 3 2))
(println x)
(def x (< 2 3))
(println x)
(def x (<= 2 3))
(println x)
(def x (> 3 2))
(println x)
(def x (>= 3 2))
(println x)
)
(Example)
true
true
true
true
true
true
33
Clojure
Logical Operators
Logical operators are used to evaluate Boolean expressions. Following are the logical
operators available in Groovy.
or This is the logical “and” operator (or true true) will give true
and This is the logical “or” operator (and true false) will give false
not This is the logical “not” operator (not false) will give true
The following code snippet shows how the various operators can be used.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def x (or true true))
(println x)
(def x (and true false))
(println x)
(def x (not true))
(println x)
)
(Example)
true
false
false
34
Clojure
Bitwise Operators
Groovy provides four bitwise operators. Following are the bitwise operators available in
Groovy.
Operator Description
0 0 0 0 0
0 1 0 1 1
1 1 1 1 0
1 0 0 1 1
The following code snippet shows how the various operators can be used.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def x (bit-and 00111100 00001101))
(println x)
(def x (bit-or 00111100 00001101))
(println x)
(def x (bit-xor 00111100 00001101))
(println x)
) (Example)
35
Clojure
576
37441
36865
Operator Precedence
As is the case with LISPs in general, there is no need to worry about operator precedence.
This is one of the benefits of S-Expressions and prefix notation. All functions evaluate left
to right and inside out. The operators in Clojure are just functions, and everything is fully
parenthesized.
36
8. Clojure - Loops Clojure
So far we have seen statements which are executed one after the other in a sequential
manner. Additionally, statements are provided in Clojure to alter the flow of control in a
program’s logic. They are then classified into flow of control statements which we will see
in detail.
While Statement
Following is the syntax of the ‘while’ statement.
(while(expression)
(do
codeblock)
)
The while statement is executed by first evaluating the condition expression (a Boolean
value), and if the result is true, then the statements in the while loop are executed. The
process is repeated starting from the evaluation of the condition in the while statement.
This loop continues until the condition evaluates to false. When the condition is false, the
loop terminates. The program logic then continues with the statement immediately
following the while statement. Following is the diagrammatic representation of this loop.
37
Clojure
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def x (atom 1))
(
while ( < @x 5 )
(do
(println @x)
(swap! x inc))
)
)
(Example)
In the above example, we are first initializing the value of ‘x’ variable to 1. Note that we
are using an atom value, which is a value which can be modified. Then our condition in
the while loop is that we are evaluating the condition of the expression to be such as ‘x’
should be less than 5. Till the value of ‘x’ is less than 5, we will print the value of ‘x’ and
then increase its value. The swap statement is used to populate the atom variable of ‘x’
with the new incremented value.
1
2
3
4
Doseq Statement
The ‘doseq’ statement is similar to the ‘for each’ statement which is found in many other
programming languages. The doseq statement is basically used to iterate over a sequence.
(doseq (sequence)
statement#1
)
38
Clojure
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(doseq [n [0 1 2]]
(println n))
)
(Example)
In the above example, we are using the doseq statement to iterate through a sequence of
values 0, 1, and 2 which is assigned to the variable n. For each iteration, we are just
printing the value to the console.
39
Clojure
0
1
2
Dotimes Statement
The ‘dotimes’ statement is used to execute a statement ‘x’ number of times.
Where value has to be a number which indicates the number of times the loop needs to
be iterated.
40
Clojure
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(dotimes [n 5]
(println n))
)
(Example)
In the above example, we are using the dotimes statement to repeat the number if times
the println statement is executed. And for each iteration it also increments the value the
variable n.
0
1
2
3
4
Loop Statement
The loop special form is not like a ‘for’ loop. The usage of loop is the same as the let
binding. However, loop sets a recursion point. The recursion point is designed to use with
recur, which means loop is always used with recur. To make a loop happen, the number
of arguments (arity) specified for recurs must coincide with the number of bindings for the
loop. That way, recur goes back to the loop.
loop [binding]
(condition
(statement)
(recur (binding))
41
Clojure
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(loop [x 10]
(when (> x 1)
(println x)
(recur (- x 2))
) )) (Example)
42
Clojure
In the above example, we are first binding the value of ‘x’ to 10 using the loop statement.
We then use the when condition clause to see if the value of ‘x’ is less than 1. We then
print the value of ‘x’ to the console and use the recur statement to repeat the loop. The
loop is repeated after the value of ‘x’ is decremented by 2.
10
8
6
4
2
43
9. Clojure - Decision Making Clojure
If Statement
The first decision-making statement is the ‘if’ statement. Following is the general form of
this statement in Clojure.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(if ( = 2 2) (println "Values are equal") (println "Values are not equal")))
(Example)
44
Clojure
The output of the above program will be “Values are equal”. In the above code example,
the ‘if’ condition is used to evaluate whether the values of 2 and 2 are equal. If they are,
then it will print the value of “Values are equal” else it will print the value of “Values are
not equal”.
If/do Expression
The ‘if-do’ expression in Clojure is used to allow multiple expressions to be executed for
each branch of the ‘if’ statement. We have seen in the classic ‘if’ statement in Clojure that
you can just have two statements, one which is executed for the true part and the other
which is for the false part. But the ‘if-do’ expression allows you to use multiple expressions.
Following is the general form of the ‘if-do’ expression.
if(condition)
(
statement #1
statement #1.1
)
(
statement #2
statement #2.1
}
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
( if ( = 2 2)
(
do(println "Both the values are equal")
(println "true")
)
( do(println "Both the values are not equal")
(println "false")
)
)
)
(Example)
45
Clojure
In the above example, the ‘if’ condition is used to evaluate whether the values of 2 and 2
are equal. If they are, then it will print the value of “Values are equal” and in addition we
are printing the value of “true”, else it will print the value of “Values are not equal” and
the value of “false”.
Nested If Statement
Sometimes there is a requirement to have multiple ‘if’ statement embedded inside of each
other, as is possible in other programming languages. In Clojure, this is made possible
with the help of using the logical ‘and’ when evaluating multiple expressions.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(
if ( and (= 2 2) (= 3 3)) (println "Values are equal") (println
"Values are not equal")
)
)
(Example)
46
Clojure
Case Statement
Clojure offers the ‘case’ statement which is similar to the ‘switch’ statement available in
the Java programming language. Following is the general form of the case statement.
case expression
value1 statement #1
value2 statement #2
valueN statement #N
statement #Default
The expression to be evaluated is placed in the ‘case’ statement. This generally will
evaluate to a value, which is used in the subsequent statements.
Each value is evaluated against that which is passed by the ‘case’ expression.
Depending on which value holds true, subsequent statement will be executed.
There is also a default statement which gets executed if none of the prior values
evaluate to be true.
47
Clojure
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def x 5)
( case x
5 (println "x is 5")
10 (println "x is 10")
(println "x is neither 5 nor 10")
)
)
(Example)
In the above example, we are first initializing a variable ‘x’ to a value of 5. We then have
a ‘case’ statement which evaluates the value of the variable ‘x’. Based on the value of the
variable, it will execute the relevant case set of statements. The last statement is the
default statement, if none of the previous statements are executed.
x is 5
Cond Statement
Clojure offers another evaluation statement called the ‘cond’ statement. This statement
takes a set of test/expression pairs. It evaluates each test one at a time. If a test returns
logical true, ‘cond’ evaluates and returns the value of the corresponding expression and
doesn't evaluate any of the other tests or expressions. ‘cond’ returns nil.
cond
(expression evaluation1) statement #1
(expression evaluation2) statement #2
(expression evaluationN) statement #N
:else statement #Default
48
Clojure
There are multiple expression evaluation defined and for each there is a statement
which gets executed.
There is also a default statement, which gets executed if none of the prior values
evaluate to be true. This is defined by the :else statement.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def x 5)
( cond
(= x 5) (println "x is 5")
(= x 10)(println "x is 10")
:else (println "x is not defined")
)
)
(Example)
In the above example, we are first initializing a variable x to a value of 5. We then have a
‘cond’ statement which evaluates the value of the variable ‘x’. Based on the value of the
variable, it will execute the relevant set of statements.
x is 5
49
10. Clojure - Functions Clojure
Clojure is known as a functional programming language, hence you would expect to see a
lot of emphasis on how functions work in Clojure. This chapter covers what all can be done
with functions in Clojure.
Defining a Function
A function is defined by using the ‘defn’ macro. Following is the general syntax of the
definition of a function.
(defn functionname
“optional documentation string”
[arguments]
(code block))
Functions can have documentation strings, which is good to describe what the function
actually does.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def x 1)
(def y 1.25)
(def str1 "Hello")
(println x)
(println y)
(println str1)
)
(Example)
50
Clojure
Anonymous Functions
An anonymous function is a function which has no name associated with it. Following is an
example of an anonymous function.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
((fn [x] (* 2 x)) 2)
)
(Example)
The above example defines a function which takes a value of ‘x’ as an argument and the
function itself multiples the value of the argument by 2.
In the following example, the function demo is defined with multiple arguments for each
function definition.
In the above example, the first function definition is a 0-arity function, since it has 0
arguements, one-param is 1-arity, and two-params is 2-arity and so on.
Variadic Functions
Variadic functions are functions that take varying number of arguments (some arguments
are optional). Function can also specify the ‘&’ ampersand symbol to take in an arbitrary
number of arguments.
(defn demo [message & others] (str message ([Link]/join " " others)))
51
Clojure
The above function declaration has the ‘&’ symbol next to the argument others, which
means that it can take an arbitrary number of arguments.
(0 2 4 6 8)
52
11. Clojure - Numbers Clojure
(def x 5)
(def y 5.25)
In Java, the following classes are attached to the numbers defined in Clojure.
To actually see that the numbers in Clojure are derived from Java classes, use the following
program to see the type of numbers assigned when using the ‘def’ command.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def x 5)
(def y 5.25)
(println (type x))
(println (type y)) )
(Example)
53
Clojure
The ‘type’ command is used to output the class associated with the value assigned to a
variable.
[Link]
[Link]
Number Tests
The following test functions are available for numbers.
zero?
Returns true if the number is zero, else false.
(zero? number)
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def x (zero? 0))
(println x)
(def x (zero? 0.0))
(println x)
(def x (zero? 1))
(println x))
(Example)
true
true
false
54
Clojure
pos?
Returns true if number is greater than zero, else false.
(pos? number)
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def x (pos? 0))
(println x)
(def x (pos? -1))
(println x)
(def x (pos? 1))
(println x))
(Example)
false
false
true
neg?
Returns true if number is less than zero, else false.
(neg? number)
(ns [Link]
(:gen-class))
;; This program displays Hello World
55
Clojure
(defn Example []
(def x (neg? -1))
(println x)
(def x (neg? 0))
(println x)
(def x (neg? 1))
(println x))
(Example)
true
true
false
even?
Returns true if the number is even, and throws an exception if the number is not an
integer.
(even? number)
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def x (even? 0))
(println x)
(def x (even? 2))
(println x)
(def x (even? 3))
(println x))
(Example)
56
Clojure
true
true
false
odd?
Returns true if the number is odd, and throws an exception if the number is not an integer.
(odd? number)
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def x (odd? 0))
(println x)
(def x (odd? 2))
(println x)
(def x (odd? 3))
(println x))
(Example)
false
false
true
number?
Returns true if the number is really a Number.
(number? number)
57
Clojure
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def x (number? 0))
(println x)
(def x (number? 0.0))
(println x)
(def x (number? :a))
(println x))
(Example)
true
true
false
integer?
Returns true if the number is an integer.
(integer? number)
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def x (integer? 0))
(println x)
(def x (integer? 0.0))
(println x)
(Example)
58
Clojure
true
false
float?
Returns true if the number is a float.
(float? number)
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def x (float? 0))
(println x)
(def x (float? 0.0))
(println x)
(Example)
false
true
59
12. Clojure - Recursion Clojure
We have seen the recur statement in an earlier topic and whereas the ‘for’ loop is
somewhat like a loop, recur is a real loop in Clojure.
If you have a programming background, you may have heard of tail recursion, which is a
major feature of functional languages. This recur special form is the one that implements
tail recursion. As the word “tail recursion” indicates, recur must be called in the tail
position. In other words, recur must be the last thing to be evaluated.
The simplest example of the recur statement is used within the ‘for’ loop. In the following
example, the recur statement is used to change the value of the variable ‘i’ and feed the
value of the variable back to the loop expression.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(loop [i 0]
(when (< i 5)
(println i)
(recur (inc i))
))
)
(Example)
0
1
2
3
4
60
13. Clojure - File I/O Clojure
Clojure provides a number of helper methods when working with I/O. It offers easier
classes to provide the following functionalities for files.
Reading files
Writing to files
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def string1 (slurp "[Link]"))
(println string1)
)
(Example)
If the file contains the following lines, they will be printed as:
line : Example1
line : Example2
61
Clojure
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(with-open [rdr ([Link]/reader "[Link]")]
(reduce conj [] (line-seq rdr)))
)
(Example)
If the file contains the following lines, they will be printed as:
line : Example1
line : Example2
62
Clojure
Following is an example.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(spit "[Link]"
"This is
a string"
)
)
In the above example, if you see the contents of the [Link] file , you will see the
contents of “This is a string”.
Following is an example that shows how the spit command can be used.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(with-open [w ([Link]/writer "[Link]" :append true)]
(.write w (str "hello" "world")
)
)
)(Example)
When the above code is executed, the line “hello world” will be present in the [Link]
file. The append:true option is to append data to the file. If this option is not specified,
then the file will be overwritten whenever data is written to the file.
63
Clojure
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(println (.exists ([Link]/file "[Link]"))
)
)
(Example)
If you enter the (read-line) command in the REPL window, you will have the chance to
enter some input in the console window.
user->(read-line)
Hello World
“Hello World”
64
14. Clojure - Strings Clojure
A String literal is constructed in Clojure by enclosing the string text in quotations. Strings
in Clojure need to be constructed using the double quotation marks such as “Hello World”.
(ns [Link]
(:gen-class))
(defn hello-world []
(println "Hello World")
(println "This is a demo application"))
(hello-world)
Hello World
This is a demo application
str
The concatenation of strings can be done by the simple str function.
Parameters - You can enter any number of string parameters which need to be
concatenated.
(ns [Link]
65
Clojure
(:gen-class))
(defn hello-world []
(println (str "Hello" "World"))
(println (str "Hello" "World" "Again")))
(hello-world)
HelloWorld
HelloWorldAgain
format
The formatting of strings can be done by the simple format function. The format function
formats a string using [Link].
Parameters – ‘fmt’ is the formatting which needs to be applied. ‘Args’ is the parameter
to which the formatting needs to be applied.
(ns [Link]
(:gen-class))
(defn hello-world []
(println (format "Hello , %s" "World"))
(println (format "Pad with leading zeros %06d" 1234)))
(hello-world)
Hello , World
Pad with leading zeros 001234
66
Clojure
count
To find the number of characters in a string, you can use the count function.
(count stringvariable)
(ns [Link]
(:gen-class))
(defn hello-world []
(println (count "Hello")))
(hello-world)
subs
Returns the substring of ‘s’ beginning at start inclusive, and ending at end (defaults to
length of string), exclusive.
Parameters – ‘S’ is the input string. ‘Start’ is the index position where to start the
substring from. ‘End’ is the index position where to end the substring.
(ns [Link]
(:gen-class))
(defn hello-world []
(println (subs "HelloWorld" 2 5))
67
Clojure
llo
Wo
compare
Returns a negative number, zero, or a positive number when ‘x’ is logically 'less than',
'equal to', or 'greater than' ‘y’. It is similar to Java [Link](y) except it also works
for nil, and mpares numbers and collections in a type-independent manner.
(compare x y)
Return Value - Returns a negative number, zero, or a positive number when ‘x’ is logically
'less than', 'equal to', or 'greater than' ‘y’.
(ns [Link]
(:gen-class))
(defn hello-world []
(println (compare "Hello" "hello"))
(println (compare "Hello" "Hello")))
(hello-world)
-32
0
68
Clojure
lower-case
Converts string to all lower-case.
(lower-case s)
(ns [Link]
(:gen-class))
(defn hello-world []
(println ([Link]/lower-case "HelloWorld"))
(println ([Link]/lower-case "HELLOWORLD")))
(hello-world)
helloworld
helloworld
upper-case
Converts string to all upper-case.
(upper-case s)
69
Clojure
(ns [Link]
(:gen-class))
(defn hello-world []
(println ([Link]/upper-case "HelloWorld"))
(println ([Link]/upper-case "helloworld")))
(hello-world)
HELLOWORLD
HELLOWORLD
join
Returns a string of all elements in collection, as returned by (seq collection), separated by
an optional separator.
Parameters – ‘sep’ is the separator for each element in the collection. ‘col’ is the collection
of elements.
(ns [Link]
(:gen-class))
(defn hello-world []
(println ([Link]/join ", " [1 2 3])))
(hello-world)
1 , 2 , 3
70
Clojure
split
Splits string on a regular expression.
Parameters – ‘str’ is the string which needs to be split. ‘reg’ is the regular expression
based on which the string split needs to happen.
(ns [Link]
(:gen-class))
(defn hello-world []
(println ([Link]/split "Hello World" #" ")))
(hello-world)
[Hello World]
Note that in the above output, both the strings “Hello” and “World” are separate strings.
split-lines
Split strings is based on the escape characters \n or \r\n.
(split-lines str)
(ns [Link]
(:gen-class))
(defn hello-world []
71
Clojure
[Hello World]
Note that in the above output, both the strings “Hello” and “World” are separate strings.
reverse
Reverses the characters in a string.
(reverse str)
(ns [Link]
(:gen-class))
(defn hello-world []
(println (reverse "Hello World")))
(hello-world)
dlroW olleH
replace
Replaces all instance of a match in a string with the replacement string.
Parameters – ‘str’ is the input string. ‘match’ is the pattern which will be used for the
matching process. ‘replacement’ will be the string which will be replaced for each pattern
match.
72
Clojure
Return Value - The string which has the replaced value as per the pattern match.
(ns [Link]
(:gen-class))
(defn hello-world []
(println ([Link]/replace "The tutorial is about Groovy" #"Groovy"
"Clojure")))
(hello-world)
trim
Removes whitespace from both ends of the string.
(trim str)
(ns [Link]
(:gen-class))
(defn hello-world []
(println ([Link]/trim " White spaces ")))
(hello-world)
White spaces
73
Clojure
triml
Removes whitespace from the left hand side of the string.
(triml str)
Return Value - The string which has the whitespaces removed from the beginning of the
string.
(ns [Link]
(:gen-class))
(defn hello-world []
(println ([Link]/triml " White spaces ")))
(hello-world)
White spaces
The above output will have the white spaces at the end of the string.
trimr
Removes whitespace from the right hand side of the string.
(trimr str)
Return Value - The string which has the white spaces removed from the end of the string.
74
Clojure
(ns [Link]
(:gen-class))
(defn hello-world []
(println ([Link]/trimr " White spaces ")))
(hello-world)
White spaces
The above output will have the white spaces at the beginning of the string.
75
15. Clojure - Lists Clojure
List is a structure used to store a collection of data items. In Clojure, the List implements
the ISeq interface. Lists are created in Clojure by using the list function.
(ns [Link]
(:gen-class))
(defn example []
(println (list 1 2 3 4)))
(example)
(1 2 3 4)
(a b c d)
list*
Creates a new list containing the items prepended to the rest, the last of which will be
treated as a sequence.
76
Clojure
Parameters – ‘listitems’ is the new list items which need to be appended. ‘lst’ is the list
to which the items need to be appended to.
Return Value - The new list with the appended list items.
(ns [Link]
(:gen-class))
(defn example []
(println (list* 1 [2,3])))
(example)
(1 2 3)
first
This function returns the first item in the list.
(first lst)
(ns [Link]
(:gen-class))
(defn example []
(println (first (list 1 2,3))))
(example)
77
Clojure
nth
This function returns the item in the ‘nth’ position in the list.
Parameters – ‘lst’ is the list of items. ‘index’ is the index position of the element, which
needs to be returned.
Return Value - The value at the index position from the list.
(ns [Link]
(:gen-class))
(defn example []
(println (nth (list 1 2,3) 0))
(println (nth (list 1 2,3) 2)))
(example)
1
3
cons
Returns a new list wherein an element is added to the beginning of the list.
Parameters – ‘element’ is the element which needs to be added to the list. ‘lst’ is the list
of items.
78
Clojure
(ns [Link]
(:gen-class))
(defn example []
(println (cons 0 (list 1 2,3))))
(example)
(0 1 2 3)
conj
Returns a new list wherein the list is at the beginning and the elements to be appended
are placed at the end.
Parameters – ‘elementlst’ is the list of items which needs to be added to the list. ‘lst’ is
the list of items.
(ns [Link]
(:gen-class))
(defn example []
(println (conj (list 1 2,3) 4 5)))
(example)
(5 4 1 2 3)
79
Clojure
rest
Returns the remaining items in the list after the first item.
(rest lst)
(ns [Link]
(:gen-class))
(defn example []
(println (rest (list 1 2,3))))
(example)
(2 3)
80
16. Clojure - Sets Clojure
Sets in Clojure are a set of unique values. Sets are created in Clojure with the help of the
set command.
(ns [Link]
(:gen-class))
(defn example []
(println (set '(1 1 2 2))))
(example)
{1,2}
sorted-set
Returns a sorted set of elements.
(sorted-set setofelements)
(ns [Link]
(:gen-class))
(defn example []
(println (sorted-set 3 2 1)))
(example)
81
Clojure
{1,2,3}
get
Returns the element at the index position.
Parameters – ‘setofelements’ is the set of elements. ‘index’ is the element at the index
position, which needs to be returned.
(ns [Link]
(:gen-class))
(defn example []
(println (get (set '(3 2 1)) 2))
(println (get (set '(3 2 1)) 1)))
(example)
2
1
contains?
Finds out whether the set contains a certain element or not.
82
Clojure
Return Value - Returns true if the element exists in the set or false if it dosen’t.
(ns [Link]
(:gen-class))
(defn example []
(println (contains? (set '(3 2 1)) 2))
(println (contains? (set '(3 2 1)) 5)))
(example)
true
false
conj
Appends an element to the set and returns the new set of elements.
(conj setofelements x)
Parameters – ‘setofelements’ is the set of elements. ‘x’ is the element which needs to be
appended to the set of elements.
Return Value - Returns the new set with the appended element.
(ns [Link]
(:gen-class))
(defn example []
(println (conj (set '(3 2 1)) 5)))
(example)
{1 3 2 5}
83
Clojure
disj
Disjoins an element from the set.
(disj setofelements x)
Parameters – ‘setofelements’ is the set of elements. ‘x’ is the element which needs to be
removed from the set.
Return Value - Returns the new set with the removed element.
(ns [Link]
(:gen-class))
(defn example []
(println (disj (set '(3 2 1)) 2)))
(example)
{1 3}
union
Disjoins an element from the set.
Parameters – ‘set1’ is the first set of elements. ‘set2’ is the second set of elements.
(ns [Link]
(:require [[Link] :as set])
(:gen-class))
(defn example []
(println (set/union #{1 2} #{3 4})))(example)
84
Clojure
Note that in the above example, you have to use the require statement to include the
[Link] class which contains the union method.
{1 4 3 2}
difference
Return a set that is the first set without elements of the remaining sets.
Parameters – ‘set1’ is the first set of elements. ‘set2’ is the second set of elements.
(ns [Link]
(:require [[Link] :as set])
(:gen-class))
(defn example []
(println (set/difference #{1 2} #{2 3})))
(example)
{1}
intersection
Return a set that is the intersection of the input sets.
Parameters – ‘set1’ is the first set of elements. ‘set2’ is the second set of elements.
85
Clojure
(ns [Link]
(:require [[Link] :as set])
(:gen-class))
(defn example []
(println (set/intersection #{1 2} #{2 3})))
(example)
{2}
subset?
Is set1 a subset of set2?
Parameters – ‘set1’ is the first set of elements. ‘set2’ is the second set of elements.
(ns [Link]
(:require [[Link] :as set])
(:gen-class))
(defn example []
(println (set/subset? #{1 2} #{2 3}))
(println (set/subset? #{1 2} #{1 2 3})))
(example)
false
true
86
Clojure
superset?
Is set1 a superset of set2?
Parameters – ‘set1’ is the first set of elements. ‘set2’ is the second set of elements.
(ns [Link]
(:require [[Link] :as set])
(:gen-class))
(defn example []
(println (set/superset? #{1 2} #{1 2 3}))
(println (set/superset? #{1 2 3} #{1 2})))
(example)
false
true
87
17. Clojure - Vectors Clojure
(ns [Link]
(:require [[Link] :as set])
(:gen-class))
(defn example []
(println (vector 1 2 3)))
(example)
[1 2 3]
vector-of
Creates a new vector of a single primitive type ‘t’, where ‘t’ is one of :int :long :float
:double :byte :short :char or :boolean. The resulting vector complies with the interface of
vectors in general, but stores the values unboxed internally.
(vector-of t setofelements)
Parameters – ‘t’ is the type which the vector elements should be. ‘Setofelements’ is the
set of elements comprised in the vector.
(ns [Link]
(:require [[Link] :as set])
(:gen-class))
88
Clojure
(defn example []
(println (vector-of :int 1 2 3)))
(example)
[1 2 3]
nth
This function returns the item in the nth position in the vector.
Parameters – ‘vec’ is the vector of items. ‘index’ is the index position of the element
which needs to be returned.
Return Value - The value at the index position from the vector.
(ns [Link]
(:gen-class))
(defn example []
(println (nth (vector 1 2,3) 0))
(println (nth (vector 1 2,3) 2)))
(example)
1
3
89
Clojure
get
Returns the element at the index position in the vector.
Parameters – ‘vec’ is the set of elements in the vector. ‘index’ is the element at the index
position which needs to be returned.
(ns [Link]
(:gen-class))
(defn example []
(println (get (vector 3 2 1) 2))
(println (get (vector 3 2 1) 1)))
(example)
1
2
conj
Appends an element to the vector and returns the new set of vector elements.
(conj vec x)
Parameters – ‘vec’ is the vector set of elements. ‘x’ is the element which needs to be
appended to the set of elements in the vector.
Return Value - Returns the new vector with the appended element.
90
Clojure
(ns [Link]
(:gen-class))
(defn example []
(println (conj (vector 3 2 1) 5)))
(example)
[3 2 1 5]
pop
For a list or queue, returns a new list/queue without the first item, for a vector, returns a
new vector without the last item.
(pop vec)
Return Value - Returns the new vector without the last item.
(ns [Link]
(:gen-class))
(defn example []
(println (pop (vector 3 2 1))))
(example)
[3 2]
91
Clojure
subvec
Returns a sub vector from a starting and ending index.
Parameters – ‘vec’ is the vector set of elements. ‘start’ is the starting index. ‘end’ is the
ending index.
Return Value - Returns the new vector from the starting to the ending index.
(ns [Link]
(:gen-class))
(defn example []
(println (subvec (vector 1 2 3 4 5 6 7) 2 5)))
(example)
[3 4 5]
92
18. Clojure - Maps Clojure
A Map is a collection that maps keys to values. Two different map types are provided -
hashed and sorted. HashMaps require keys that correctly support hashCode and equals.
SortedMaps require keys that implement Comparable, or an instance of Comparator.
A map can be created in two ways, the first is via the hash-map method.
Creation - HashMaps
HashMaps have a typical key value relationship and is created by using hash-map function.
(ns [Link]
(:gen-class))
(defn example []
(def demokeys (hash-map "z" "1" "b" "2" "a" "3"))
(println demokeys))
(example)
{z 1, b 2, a 3}
Creation - SortedMaps
SortedMaps have the unique characteristic of sorting their elements based on the key
element. Following is an example that shows how the sorted map can be created using
the sorted-map function.
(ns [Link]
(:gen-class))
(defn example []
(def demokeys (sorted-map "z" "1" "b" "2" "a" "3"))
(println demokeys))
(example)
93
Clojure
{a 3, b 2, z 1}
From the above program you can clearly see that elements in the maps are sorted as per
the key value. Following are the methods available for maps.
get
Returns the value mapped to key, not-found or nil if key is not present.
Parameters – ‘hmap’ is the map of hash keys and values. ‘key’ is the key for which the
value needs to be returned.
Return Value - Returns the value of the key passed to the get function.
(ns [Link]
(:gen-class))
(defn example []
(def demokeys (hash-map "z" "1" "b" "2" "a" "3"))
(println demokeys)
(println (get demokeys "b")))
(example)
{z 1, b 2, a 3}
2
94
Clojure
contains?
See whether the map contains a required key.
Parameters – ‘hmap’ is the map of hash keys and values. ‘key’ is the key which needs to
be searched in the map.
Return Value - Returns the value of true if the key is present, else returns false.
(ns [Link]
(:gen-class))
(defn example []
(def demokeys (hash-map "z" "1" "b" "2" "a" "3"))
(println (contains? demokeys "b"))
(println (contains? demokeys "x")))
(example)
true
false
find
Returns the map entry for the key.
Parameters – ‘hmap’ is the map of hash keys and values. ‘key’ is the key which needs to
be searched in the map.
Return Value - Returns the key value pair for the desired key, else returns nil.
95
Clojure
(ns [Link]
(:gen-class))
(defn example []
(def demokeys (hash-map "z" "1" "b" "2" "a" "3"))
(println demokeys)
(println (find demokeys "b"))
(println (find demokeys "x")))
(example)
[b 2]
nil
keys
Returns the list of keys in the map.
(keys hmap)
(ns [Link]
(:gen-class))
(defn example []
(def demokeys (hash-map "z" "1" "b" "2" "a" "3"))
(println (keys demokeys)))
(example)
96
Clojure
(z a b)
vals
Returns the list of values in the map.
(vals hmap)
(ns [Link]
(:gen-class))
(defn example []
(def demokeys (hash-map "z" "1" "b" "2" "a" "3"))
(println (vals demokeys)))
(example)
(1 3 2)
dissoc
Dissociates a key value entry from the map.
Parameters – ‘hmap’ is the map of hash keys and values. ‘key’ is the key which needs to
be dissociated from the HashMap.
97
Clojure
(ns [Link]
(:gen-class))
(defn example []
(def demokeys (hash-map "z" "1" "b" "2" "a" "3"))
(println (dissoc demokeys "b")))
(example)
{z 1, a 3}
merge
Merges two maps entries into one single map entry.
Parameters – ‘hmap1’ is the map of hash keys and values. ‘hmap2’ is the map of hash
keys and values, which needs to be mapped with the first HashMap.
(ns [Link]
(:gen-class))
(defn example []
(def demokeys (hash-map "z" "1" "b" "2" "a" "3"))
(def demokeys1 (hash-map "x" "4" "h" "5" "i" "7"))
(println (merge demokeys demokeys1)))
(example)
{z 1, x 4, a 3, i 7, b 2, h 5}
98
Clojure
merge-with
Returns a map that consists of the rest of the maps conj-ed onto the first. If a key occurs
in more than one map, the mapping(s) from the latter (left-to-right) will be combined with
the mapping in the result.
Parameters – ‘f’ is the operator which needs to be applied to the hash maps. ‘hmap1’ is
the map of hash keys and values. ‘hmap2’ is the map of hash keys and values, which
needs to be mapped with the first HashMap.
Return Value - Returns a map that consists of the rest of the maps conj-ed onto the first.
(ns [Link]
(:gen-class))
(defn example []
(def demokeys (hash-map "z" 1 "b" 2 "a" 3))
(def demokeys1 (hash-map "a" 2 "h" 5 "i" 7))
(println (merge-with + demokeys demokeys1)))
(example)
{z 1, a 5, i 7, b 2, h 5}
Notice that in the output since the key ‘a’ occurs twice, the value is added from both
HashMaps as per the operator +.
select-keys
Returns a map containing only those entries in map whose key is in keys.
99
Clojure
Parameters – ‘hmap’ is the map of hash keys and values. ‘keys’ is the list of keys which
need to be selected from the HashMap.
Return Value - Returns the keys from the map as per the select clause of keys.
(ns [Link]
(:gen-class))
(defn example []
(def demokeys (hash-map "z" 1 "b" 2 "a" 3))
(println (select-keys demokeys ["z" "a"])))
(example)
{z 1, a 3}
rename-keys
Renames keys in the current HashMap to the newly defined ones.
Parameters – ‘hmap’ is the map of hash keys and values. ‘keys’ is the new list of keys
which need to be replaced in the map.
(ns [Link]
(:require [[Link] :as set])
(:gen-class))
(defn example []
(def demokeys (hash-map "z" 1 "b" 2 "a" 3))
(def demonew (set/rename-keys demokeys {"z" "newz" "b" "newb" "a" "newa"}))
(println demonew))
(example)
100
Clojure
map-invert
Inverts the maps so that the values become the keys and vice versa.
(map-invert hmap)
Return Value - Returns a map with the values inverted to the keys and the keys inverted
to values.
(ns [Link]
(:require [[Link] :as set])
(:gen-class))
(defn example []
(def demokeys (hash-map "z" 1 "b" 2 "a" 3))
(def demonew (set/map-invert demokeys))
(println demonew))
(example)
{1 z, 3 a, 2 b}
101
19. Clojure - Namespaces Clojure
Namespaces in Clojure are used to differentiate classes into separate logical spaces just
like in Java. Consider the following statement.
In the above statement, ‘[Link]’ is a namespace which contains various classes and
methods to be used in the program. For example, the above namespace contains the
function called map-invert, which is used to invert a map of key-values. We cannot use
this function unless we explicitly tell our program to include this namespace.
*ns*
This is used to look at your current namespace.
(*ns*)
Parameters – None.
(ns [Link]
(:require [[Link] :as set])
(:gen-class))
(defn example []
(println *ns*))
(example)
102
Clojure
As you can see the output of the above program displays the namespace as
[Link] which is the name of the current namespace.
ns
This is used to create a new namespace and associate it with the running program.
(ns namespace-name)
(ns [Link]
(:require [[Link] :as set])
(:gen-class))
(defn hello-world []
(println *ns*))
(hello-world)
alias
Add an alias in the current namespace to another namespace. Arguments are two symbols:
the alias to be used and the symbolic name of the target namespace.
Parameters – ‘aliasname’ is the alias name which needs to be associated with the
namespace. ‘namespace-name’ is the namespace which is associated with the running
program.
103
Clojure
(ns [Link]
(:require [[Link] :as set])
(:gen-class))
(defn example []
(alias 'string '[Link])
(example)
all-ns
Returns a list of all namespaces.
(all-ns)
Parameters – None.
(ns [Link]
(:require [[Link] :as set])
(:gen-class))
(defn example []
(println (all-ns)))
(example)
104
Clojure
find-ns
Finds and returns a particular namespace.
(find-ns namespace-name)
(ns [Link]
(:require [[Link] :as set])
(:gen-class))
(defn example []
(println (find-ns '[Link])))
(example)
ns-name
Returns the name of a particular namespace.
(ns-name namespace-name)
(ns [Link]
(:require [[Link] :as set])
(:gen-class))
105
Clojure
(defn example []
(println (ns-name '[Link])))
(example)
[Link]
ns-aliases
Returns the aliases, which are associated with any namespaces.
(ns-aliases namespace-name)
Return Value - Returns the aliases which are associated with any namespaces.
(ns [Link]
(:require [[Link] :as set])
(:gen-class))
(defn example []
(println (ns-aliases '[Link])))
(example)
106
Clojure
ns-map
Returns a map of all the mappings for the namespace.
(ns-map namespace-name)
Return Value - Returns a map of all the mappings for the namespace.
(ns [Link]
(:require [[Link] :as set])
(:gen-class))
(defn example []
(println (count (ns-map '[Link]))))
(example)
848
The count method is used to provide the total count of maps returned.
107
Clojure
un-alias
Removes the alias for the symbol from the namespace.
‘aliasname’ is the alias name which has been mapped to the namespace.
(ns [Link]
(:gen-class))
(defn example []
(alias 'string '[Link])
(ns-unalias '[Link] 'string))
(example)
108
20. Clojure - Exception Handling Clojure
Let’s consider the following program which does an operation on a file called [Link].
However, there can be always a case wherein the file [Link] does not exist.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def string1 (slurp "[Link]"))
(println string1)
)
(Example)
If the file [Link] does not exist, then the following exception will be generated by
the program.
From the above exception, we can clearly see that the program raised a
FileNotFoundException.
109
Clojure
One classical case is the ArrayIndexOutOfBoundsException which happens when you try
to access an index of an array which is greater than the length of the array. Following is a
typical example of this sort of mistake.
(ns [Link]
(:gen-class))
(defn Example []
(try
(aget (int-array [1 2 3]) 5)
(catch Exception e (println (str "caught exception: " (.toString e))))
(finally (println "This is our final block")))
(println "Let's move on")
)
(Example)
When the above code is executed, the following exception will be raised.
Error
Error is irrecoverable e.g. OutOfMemoryError, VirtualMachineError, AssertionError, etc.
These are errors which the program can never recover from and will cause the program
to crash. We now need some mechanism to catch these exceptions so that the program
can continue to run if these exceptions exist.
110
Clojure
The following diagram shows how the hierarchy of exceptions in Clojure is organized. It’s
all based on the hierarchy defined in Java.
Catching Exceptions
Just like other programming languages, Clojure provides the normal ‘try-catch‘ block to
catch exceptions as and when they occur.
(try
(
//Protected code
)
catch Exception e1)
(
//Catch block
) )
111
Clojure
All of your code which could raise an exception is placed in the Protected code block.
In the catch block, you can write custom code to handle your exception so that the
application can recover from the exception.
Let’s look at our earlier example which generated a file-not-found exception and see how
we can use the try catch block to catch the exception raised by the program.
(ns [Link]
(:gen-class))
(defn Example []
(try
(def string1 (slurp "[Link]"))
(println string1)
(catch Exception e (println (str "caught exception: " (.getMessage
e)))))
)
(Example)
From the above code, we wrap out faulty code in the try block. In the catch block, we are
just catching our exception and outputting a message that an exception has occurred. So,
we now have a meaningful way of capturing the exception, which is generated by the
program.
Let’s modify our earlier code to include two catch blocks, one which is specific for our file
not found exception and the other is for a general exception block.
(ns [Link]
(:gen-class))
(defn Example []
(try
(def string1 (slurp "[Link]"))
112
Clojure
(println string1)
(catch [Link] e (println (str "caught file
exception: " (.getMessage e))))
(catch Exception e (println (str "caught exception: " (.getMessage
e)))))
(println "Let's move on")
)
(Example)
From the above output, we can clearly see that our exception was caught by the
‘FileNotFoundException’ catch block and not the general one.
Finally Block
The finally block follows a try block or a catch block. A finally block of code always
executes, irrespective of occurrence of an Exception.
Using a finally block allows you to run any cleanup-type statements that you want to
execute, no matter what happens in the protected code. Following is the syntax for this
block.
(try
(
//Protected code
)
catch Exception e1)
(
//Catch block
)
(finally
//Cleanup code
)
)
113
Clojure
Let’s modify the above code and add the finally block of code. Following is the code snippet.
(ns [Link]
(:gen-class))
(defn Example []
(try
(def string1 (slurp "[Link]"))
(println string1)
(catch [Link] e (println (str "caught file
exception: " (.getMessage e))))
(catch Exception e (println (str "caught exception: " (.getMessage
e))))
(finally (println "This is our final block")))
(println "Let's move on")
)
(Example)
From the above program, you can see that the final block is also implemented after the
catch block catches the required exception.
Since Clojure derives its exception handling from Java, similar to Java, the following
methods are available in Clojure for managing the exceptions.
public String toString() - Returns the name of the class concatenated with the
result of getMessage().
public void printStackTrace() - Prints the result of toString() along with the
stack trace to [Link], the error output stream.
114
Clojure
call stack, and the last element in the array represents the method at the bottom
of the call stack.
Following is the example code that uses some of the methods listed above.
(ns [Link]
(:gen-class))
(defn Example []
(try
(def string1 (slurp "[Link]"))
(println string1)
(catch [Link] e (println (str "caught file
exception: " (.toString e))))
(catch Exception e (println (str "caught exception: " (.toString e))))
(finally (println "This is our final block")))
(println "Let's move on")
)
(Example)
115
21. Clojure - Sequences Clojure
Sequences are created with the help of the ‘seq’ command. Following is a simple example
of a sequence creation.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(println (seq [1 2 3]
)))
(Example)
(1 2 3)
cons
Returns a new sequence where ‘x’ is the first element and ‘seq’ is the rest.
(cons x seq)
Parameters – ‘x’ is the element which needs to be added to the sequence. ‘seq’ is the
sequence list of elements.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(println (cons 0 (seq [1 2 3] ) ))) (Example)
116
Clojure
(0 1 2 3)
conj
Returns a new sequence where ‘x’ is the element that is added to the end of the sequence.
(conj seq x)
Parameters – ‘x’ is the element which needs to be added to the sequence. ‘seq’ is the
sequence list of elements.
Following is an example of conj in Clojure. Note that in the following program, we are also
seeing the shorter version of creating a sequence, which can simply be done by using the
square brackets [].
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(println (conj [1 2 3] 4)))
(Example)
(1 2 3 4)
concat
This is used to concat two sequences together.
Parameters – ‘seq1’ is the first sequence list of elements. ‘seq2’ is the second sequence
list of elements, which needs to be appended to the first.
117
Clojure
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def seq1 (seq [1 2]))
(def seq2 (seq [3 4]))
(println (concat seq1 seq2)))
(Example)
(1 2 3 4)
distinct
Used to only ensure that distinct elements are added to the sequence.
(distinct seq1)
Return Value - The sequence of elements wherein only distinct elements are returned.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def seq1 (distinct (seq [1 1 2 2])))
(println seq1))
(Example)
118
Clojure
(1 2)
reverse
Reverses the elements in the sequence.
(reverse seq1)
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def seq1 (reverse (seq [1 2 3])))
(println seq1))
(Example)
(3 2 1)
first
Returns the first element of the sequence.
(first seq1)
119
Clojure
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def seq1 (seq [1 2 3]))
(println (first seq1)))
(Example)
last
Returns the last element of the sequence.
(last seq1)
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def seq1 (seq [1 2 3]))
(println (last seq1)))
(Example)
120
Clojure
rest
Returns the entire sequence except for the first element
(rest seq1)
Return Value - Returns the entire sequence except for the first element.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def seq1 (seq [1 2 3 4 5]))
(println (rest seq1)))
(Example)
(2 3 4 5)
sort
Returns a sorted sequence of elements.
(sort seq1)
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
121
Clojure
(1 2 3 4 5)
drop
Drops elements from a sequence based on the number of elements, which needs to be
removed.
Parameters – ‘seq1’ is the sequence list of elements. ‘num’ is the number of elements
which need to be dropped.
Return Value - Returns the sequence of elements with the required elements dropped
from the sequence.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def seq1 (seq [5 4 3 2 1]))
(def seq2 (drop 2 seq1))
(println seq2))
(Example)
(3 2 1)
122
Clojure
take-last
Takes the last list of elements from the sequence.
Parameters – ‘seq1’ is the sequence list of elements. ‘num’ is the number of elements
which needs to be included in the sequence from the end.
Return Value - A new sequence of elements with only the end number of elements
included.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def seq1 (seq [5 4 3 2 1]))
(def seq2 (take-last 2 seq1))
(println seq2))
(Example)
(2 1)
take
Takes the first list of elements from the sequence.
Parameters – ‘seq1’ is the sequence list of elements. ‘num’ is the number of elements
which needs to be included in the sequence from the beginning.
Return Value - A new sequence of elements with only the beginning number of elements
included.
123
Clojure
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def seq1 (seq [5 4 3 2 1]))
(def seq2 (take 2 seq1))
(println seq2))
(Example)
(5 4)
split-at
Splits the sequence of items into two parts. A location is specified at which the split should
happen.
Parameters – ‘seq1’ is the sequence list of elements. ‘num’ is the index position at which
the split should happen.
Return Value – Two sequence of elements which are split based on the location where
the split should happen.
124
Clojure
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def seq1 (seq [5 4 3 2 1]))
(println (split-at 2 seq1)))
(Example)
[(5 4) (3 2 1)]
125
22. Clojure - Regular Expressions Clojure
//d+
The above regular expression is used to find one more occurrence of a digit in a string.
The // characters are used to ensure that characters ‘d’ and ‘+’ are used to represent a
regular expression.
There are two special positional characters that are used to denote the beginning
and end of a line: caret (∧) and dollar sign ($):
Regular expressions can also include quantifiers. The plus sign (+) represents one
or more times, applied to the preceding element of the expression. The asterisk (*)
is used to represent zero or more occurrences. The question mark (?) denotes zero
or once.
In a regular expression, the period symbol (.) can represent any character. This is
described as the wildcard character.
126
Clojure
re-pattern
Returns an instance of [Link]. This is then used in further methods for
pattern matching.
(re-pattern pat)
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def pat (re-pattern "\\d+")))
(Example)
The above program will create a pattern object, which will be a pattern of one or more
digits.
re-find
Returns the next regex match, if any, of string to pattern, using
[Link]()
Parameters – ‘pat’ is the pattern which needs to be formed. ‘str’ is the string in which
text needs to be found based on the pattern.
Return Value - A string if a match is found based on the input string and pattern.
(ns [Link]
(:gen-class))
127
Clojure
123
replace
The replace function is used to replace a substring in a string with a new string value. The
search for the substring is done with the use of a pattern.
Parameters – ‘pat’ is the regex pattern. ‘str’ is the string in which a text needs to be
found based on the pattern. ‘replacestr’ is the string which needs to be replaced in the
original string based on the pattern.
Return Value - The new string in which the replacement of the substring is done via the
regex pattern.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def pat (re-pattern "\\d+"))
(def newstr ([Link]/replace "abc123de" pat "789"))
(println newstr))
(Example)
abc789de
128
Clojure
replace-first
The replace function is used to replace a substring in a string with a new string value, but
only for the first occurrence of the substring. The search for the substring is done with the
use of a pattern.
Parameters – ‘pat’ is the regex pattern. ‘str’ is the string in which a text needs to be
found based on the pattern. ‘replacestr’ is the string which needs to be replaced in the
original string based on the pattern.
Return Value - The new string in which the replacement of the substring is done via the
regex pattern, but only with the first occurrence.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def pat (re-pattern "\\d+"))
(def newstr1 ([Link]/replace "abc123de123" pat "789"))
(def newstr2 ([Link]/replace-first "abc123de123" pat "789"))
(println newstr1)
(println newstr2))
(Example)
The above example shows the difference between the replace and replace-first function.
abc789de789
abc789de123
129
23. Clojure - Predicates Clojure
Predicates are functions that evaluate a condition and provide a value of either true or
false. We have seen predicate functions in the examples of the chapter on numbers. We
have seen functions like ‘even?’ which is used to test if a number is even or not, or ‘neg?’
which is used to test if a number is greater than zero or not. All of these functions return
either a true or false value.
(ns [Link]
(:gen-class))
;; This program displays Hello World
(defn Example []
(def x (even? 0))
(println x)
(def x (neg? 2))
(println x)
(def x (odd? 3))
(println x)
(def x (pos? 3))
(println x))
(Example)
true
false
true
true
In addition to the normal predicate functions, Clojure provides more functions for
predicates. The following methods are available for predicates.
130
Clojure
every-pred
Takes a set of predicates and returns a function ‘f’ that returns true if all of its composing
predicates return a logical true value against all of its arguments, else it returns false.
(every-pred p1 p2 .. pn)
Parameters – ‘p1 p2...pn’ is the list of all predicates which need to be tested.
Return Value - Returns true if all of its composing predicates return a logical true value
against all of its arguments, else it returns false.
(ns [Link]
(:gen-class))
(defn Example []
(println ((every-pred number? even?) 2 4 6))
(println ((every-pred number? odd?) 2 4 6)))
(Example)
true
false
every?
Returns true if the predicate is true for every value, else false.
(every? p1 col)
Parameters – ‘p1’ is the predicate which need to be tested. ‘col’ is the collection of values
which needs to be tested.
Return Value - Returns true if the predicate is true for every value, else false.
131
Clojure
(ns [Link]
(:gen-class))
(defn Example []
(println (every? even? '(2 4 6)))
(println (every? odd? '(2 4 6))))
(Example)
true
false
some
Returns the first logical true value for any predicate value of x in the collection of values.
(some p1 col)
Parameters – ‘p1’ is the predicate which needs to be tested. ‘col’ is the collection of
values which needs to be tested.
Return Value - Returns true if the predicate is true for every value, else false.
(ns [Link]
(:gen-class))
(defn Example []
(println (some even? '(1 2 3 4))))
(Example)
true
132
Clojure
Note that in the above program, once the predicate reaches the value 2, which is even,
the function will exit and the values of 3 and 4 will not be tested.
not-any?
Returns false if any of the predicates of the values in a collection are logically true, else
true.
(not-any? p1 col)
Parameters – ‘p1’is the predicate which needs to be tested. ‘col’ is the collection of values
which needs to be tested.
Return Value - Returns false if any of the predicates of the values in a collection are
logically true, else true.
(ns [Link]
(:gen-class))
(defn Example []
(println (not-any? even? '(2 4 6))))
(Example)
false
133
24. Clojure - Destructuring Clojure
Destructuring is a functionality within Clojure, which allows one to extract values from a
data structure, such as a vector and bind them to symbols without having to explicitly
traverse the datastructure.
Let’s look at an example of what exactly Destructuring means and how does it happen.
(ns [Link]
(:gen-class))
(defn Example []
(def my-vector [1 2 3 4])
(let [[a b c d] my-vector]
(println a b c d)))
(Example)
1 2 3 4
We are then using the ‘let’ statement to assign 4 variables (a, b, c, and d) to the
my-vector variable directly.
If we run the ‘println’ statement on the four variables, we can see that they have
already been assigned to the values in the vector respectively.
So clojure has destructured the my-vector variable which has four values when it was
assigned using the ‘let’ statement. The deconstructed four values were then assigned to
the four parameters accordingly.
If there are excess variables which don’t have a corresponding value to which they can be
assigned to, then they will be assigned the value of nil. The following example makes this
point clear.
(ns [Link]
(:gen-class))
(defn Example []
(def my-vector [1 2 3 4])
(let [[a b c d e] my-vector]
134
Clojure
(println a b c d e)))
(Example)
The above program produces the following output. You can see from the output that since
the last variable ‘e’ does not have a corresponding value in the vector, it accounts to nil.
1 2 3 4 nil
the-rest
The ‘the-rest’ variable is used to store the remaining values, which cannot get assigned to
any variable.
(ns [Link]
(:gen-class))
(defn Example []
(def my-vector [1 2 3 4])
(let [[a b & the-rest] my-vector]
(println a b the-rest)))
(Example)
The above program produces the following output. From the output, you can clearly see
that the values of 3 and 4 cannot be assigned to any variable so they are assigned to the
‘the-rest’ variable.
1 2 (3 4)
Destructuring Maps
Just like vectors, maps can also be destructured. Following is an example of how this can
be accomplished.
(ns [Link]
(:gen-class))
(defn Example []
(def my-map {"a" 1 "b" 2})
(let [{a "a" b "b"} my-map]
(println a b))) (Example)
135
Clojure
The above program produces the following output. From the program you can clearly see
that the map values of “a” and “b” are assigned to the variables of a and b.
1 2
Similarly in the case of vectors, if there is no corresponding value in the map when the
destructuring happens, then the variable will be assigned a value of nil.
Following is an example.
(ns [Link]
(:gen-class))
(defn Example []
(def my-map {"a" 1 "b" 2})
(let [{a "a" b "b" c "c"} my-map]
(println a b c)))
(Example)
1 2 nil
136
25. Clojure - Date & Time Clojure
Since the Clojure framework is derived from Java classes, one can use the date-time
classes available in Java in Clojure. The class date represents a specific instant in time,
with millisecond precision.
[Link]
This is used to create the date object in Clojure.
[Link].
Parameters – None.
Return Value - Allocates a Date object and initializes it so that it represents the time at
which it was allocated, measured to the nearest millisecond.
(ns example)
(defn Example []
(def date (.toString ([Link].)))
(println date))
(Example)
The above program produces the following output. This will depend on the current date
and time on the system, on which the program is being run.
[Link]
This is used to format the date output
137
Clojure
Parameters – ‘format’ is the format to be used when formatting the date. ‘dt’ is the date
which needs to be formatted.
(ns example)
(defn Example []
(def date (.format ([Link]. "MM/dd/yyyy") (new
[Link])))
(println date))
(Example)
The above program produces the following output. This will depend on the current date
and time on the system, on which the program is being run.
02/06/2016
getTime
Returns the number of milliseconds since January 1, 1970, [Link] GMT represented by
this Date object.
(.getTime)
Parameters – None.
Return Value - The number of milliseconds since January 1, 1970, [Link] GMT
represented by this date.
(ns example)
(import [Link])
(defn Example []
(def date (.getTime ([Link].)))
(println date))
(Example)
The above program produces the following output. This will depend on the current date
and time on the system, on which the program is being run.
1454794676995
138
26. Clojure - Atoms Clojure
Atoms are a data type in Clojure that provide a way to manage shared, synchronous,
independent state. An atom is just like any reference type in any other programming
language. The primary use of an atom is to hold Clojure’s immutable data structures. The
value held by an atom is changed with the swap! method.
Internally, swap! reads the current value, applies the function to it, and attempts to
compare-and-set it in. Since another thread may have changed the value in the
intervening time, it may have to retry, and does so in a spin loop. The net effect is that
the value will always be the result of the application of the supplied function to a current
value, atomically.
Atoms are created with the help of the atom method. An example on the same is shown
in the following program.
(ns [Link]
(:gen-class))
(defn example []
(def myatom (atom 1))
(println @myatom))
(example)
The value of atom is accessed by using the @ symbol. Clojure has a few operations that
can be performed on atoms. Following are the operations.
reset!
Sets the value of atom to a new value without regard for the current value.
Parameters – ‘atom-name’ is the name of the atom whose value needs to be reset.
‘newvalue’ is the new value, which needs to be assigned to the atom.
(ns [Link]
(:gen-class))
(defn example []
(def myatom (atom 1))
(println @myatom)
(reset! myatom 2)
(println @myatom))
(example)
1
2
compare-and-set!
Atomically sets the value of atom to the new value if and only if the current value of the
atom is identical to the old value held by the atom. Returns true if set happens, else it
returns false.
Parameters – ‘atom-name’ is the name of the atom whose value needs to be reset.
‘oldvalue’ is the current old value of the atom. ‘newvalue’ is the new value which needs to
be assigned to the atom.
Return Value - The atom with the new value will be set only if the old value is specified
properly.
140
Clojure
(ns [Link]
(:gen-class))
(defn example []
(def myatom (atom 1))
(println @myatom)
(compare-and-set! myatom 0 3)
(println @myatom)
(compare-and-set! myatom 1 3)
(println @myatom)
)
(example)
1
1
3
swap!
Atomically swaps the value of the atom with a new one based on a particular function.
Parameters – ‘atom-name’ is the name of the atom whose value needs to be reset.
‘function’ is the function which is used to generate the new value of the atom.
Return Value - The atom with the new value will be set based on the function provided.
141
Clojure
(ns [Link]
(:gen-class))
(defn example []
(def myatom (atom 1))
(println @myatom)
(swap! myatom inc)
(println @myatom)
)
(example)
1
2
From the above program you can see that the ‘inc’ (Increment function) is used to
increment the value of the atom and with the help of the swap! function, the new value is
automatically associated with the atom.
142
27. Clojure - Metadata Clojure
In Clojure, metadata is used to annotate the data in a collection or for the data stored in
a symbol. This is normally used to annotate data about types to the underlying compiler,
but can also be used for developers. Metadata is not considered as part of the value of the
object. At the same time, metadata is immutable.
meta-with
This function is used to define a metadata map for any object.
Parameters – ‘obj’ is the object with which metadata needs to be associated with.
‘mapentry’ is the metadata which needs to be associated with the object.
Return Value - Returns an object of the same type and value as obj, with mapentry as
its metadata.
(ns [Link]
(:gen-class))
(defn Example []
(def my-map (with-meta [1 2 3] {:prop "values"}))
(println (meta my-map)))
(Example)
{:prop values}
143
Clojure
meta
This function is used to see if any metadata is associated with an object.
(meta obj)
Parameters – ‘obj’ is the object which needs to be checked if any metadata is associated
with it.
Return Value - Returns the metadata of obj, returns nil if there is no metadata.
(ns [Link]
(:gen-class))
(defn Example []
(def my-map (with-meta [1 2 3] {:prop "values"}))
(println (meta my-map)))
(Example)
{:prop values}
vary-meta
Returns an object of the same type and value as the original object, but with a combined
metadata.
Parameters – ‘obj’ is the object which needs to be checked if any metadata is associated
with it. ‘new-meta’ is the metadata values which needs to be associated with the object.
Return Value - Returns an object of the same type and value as the original object, but
with a combined metadata.
144
Clojure
(ns [Link]
(:gen-class))
(defn Example []
(def my-map (with-meta [1 2 3] {:prop "values"}))
(println (meta my-map))
(def new-map (vary-meta my-map assoc :newprop "new values"))
(println (meta new-map)))
(Example)
{:prop values}
{:prop values, :newprop new values}
145
28. Clojure - StructMaps Clojure
StructMaps are used for creating structures in Clojure. For example, if you wanted to
create a structure which comprised of an Employee Name and Employeeid, you can do
that with StructMaps.
defstruct
This function is used for defining the structure which is required.
Parameters – ‘structname’ is the name to be given to the structure. ‘keys’ is the keys
which needs to be a part of the structure.
(ns [Link]
(:gen-class))
(defn Example []
(println (defstruct Employee :EmployeeName :Employeeid)))
(Example)
Note that the above function is only used to create your structure, we will see more
functions which can be used to work with structures.
#'[Link]/Employee
struct
This function is used to define a structure object of the type, which is created by the
defstruct operation.
146
Clojure
Parameters – ‘structname’ is the name to be given to the structure. ‘values’ is the values
which needs to be assigned to the key values of the structure.
Return Value - Returns a struct object with the values mapped to the keys of the
structure.
(ns [Link]
(:gen-class))
(defn Example []
(defstruct Employee :EmployeeName :Employeeid)
(def emp (struct Employee "John" 1))
(println emp))
(Example)
It can be clearly seen that the values supplied in the struct function was assigned to the
keys for the Employee object.
struct-map
This function is used to specifically assign values to key values by explicitly defining which
values get assigned to which keys in the structure.
Parameters – ‘structname’ is the name to be given to the structure. ‘keyn and valuen’
are the key values which needs to be assigned to the structure.
Return Value - Returns a struct object with the values mapped to the keys of the
structure.
147
Clojure
(ns [Link]
(:gen-class))
(defn Example []
(defstruct Employee :EmployeeName :Employeeid)
(def emp (struct-map Employee :EmployeeName "John" :Employeeid 1))
(println emp))
(Example)
:key structure-name
Return Value - The value associated with the key will be returned.
(ns [Link]
(:gen-class))
(defn Example []
(defstruct Employee :EmployeeName :Employeeid)
(def emp (struct-map Employee :EmployeeName "John" :Employeeid 1))
(println (:Employeeid emp))
(println (:EmployeeName emp)))
(Example)
148
Clojure
1
John
Immutable Nature
By default structures are also immutable, so if we try to change the value of a particular
key, it will not change.
(ns [Link]
(:gen-class))
(defn Example []
(defstruct Employee :EmployeeName :Employeeid)
(def emp (struct-map Employee :EmployeeName "John" :Employeeid
1))
(println (:EmployeeName emp))
(assoc emp :EmployeeName "Mark")
(println (:EmployeeName emp)))
(Example)
In the above example, we try to use the ‘assoc’ function to associate a new value for the
Employee Name in the structure.
John
John
This clearly shows that the structure is immutable. The only way to change the value is to
create a new variable with the changed value as shown in the following program.
(ns [Link]
(:gen-class))
(defn Example []
(defstruct Employee :EmployeeName :Employeeid)
(def emp (struct-map Employee :EmployeeName "John" :Employeeid 1))
(def newemp (assoc emp :EmployeeName "Mark"))
149
Clojure
(println newemp))
(Example)
(ns [Link]
(:gen-class))
(defn Example []
(defstruct Employee :EmployeeName :Employeeid)
(def emp (struct-map Employee :EmployeeName "John" :Employeeid 1))
(def newemp (assoc emp :EmployeeRank "A"))
(println newemp))
(Example)
In the above example, we associate a new key called EmployeeRank to the structure, but
by creating a new structure object.
150
29. Clojure - Agents Clojure
As pointed out many times, Clojure is a programming language wherein many of the data
types are immutable, which means that the only way one can change the value of a
variable is to create a new variable and assign the new value to it. However, Clojure does
provide some elements, which can create an mutable state. We have seen that this can
be achieved with the atom data type. The other way this can be achieved is via Agents.
agent
An agent is created by using the agent command.
(agent state)
Parameters – ‘state’ is the initial state that should be assigned to the agent.
Return Value - Returns an agent object with a current state and value.
(ns [Link]
(:gen-class))
(defn Example []
(def counter (agent 0))
(println counter))
(Example)
151
Clojure
Just like the atom data type, you can see that the agent also has a status and a value
associated with it. To access the value of the agent directly you need to use the @symbol
along with the variable name.
(ns [Link]
(:gen-class))
(defn Example []
(def counter (agent 0))
(println @counter))
(Example)
You can clearly see from the above program that if you have append the @ symbol like
@counter, you will get access to the value of the agent variable.
send
This function is used to send across a value to the agent.
Parameters – ‘agentname’ is the agent to which the send function is being redirected to.
The ‘function’ is used to determine which way the value of the agent will be changed. In
our case, we will use the addition + symbol to add a value to the existing value of the
agent. ‘Value’ is the value passed to the function, which in turn will be used to update the
value of the agent accordingly.
152
Clojure
(ns [Link]
(:gen-class))
(defn Example []
(def counter (agent 0))
(println @counter)
(send counter + 100)
(println "Incrementing Counter")
(println @counter))
(Example)
0
Incrementing Counter
0
Since the send function is an asynchronous function, there is a time delay for when
the value of the agent is updated. This is why we have added an extra ‘println’
statement to the program. This is to give the Clojure environment the time required
to update the agent value accordingly.
Secondly, when you run the above program, the program will not terminate
immediately. This is because the Clojure environment does not know whether it is
safe to shut down the agent. We will see how to shut down agents in the next
function description.
shutdown-agents
This function is used to shut down any running agents.
(shutdown-agents)
Parameters – None.
153
Clojure
(ns [Link]
(:gen-class))
(defn Example []
(def counter (agent 0))
(println @counter)
(send counter + 100)
(println "Incrementing Counter")
(println @counter)
(shutdown-agents))
(Example)
0
Incrementing Counter
0
The key difference in the above program is that, the program will now terminate since all
agents will shut down properly.
send-off
There are instances wherein an agent is assigned a function which is blocking in nature. A
simple example is, consider you are reading contents from a file which itself is blocking in
nature. So the send-off function will first immediately return the agent and continue with
the file operation. When the file operation completes, it will automatically update the agent
with the contents of the file.
Parameters – ‘agentname’ is the agent to which the send function is being redirected to.
The ‘function’ is used to determine which way the value of the agent will be changed. In
our case, we will use the addition + symbol to add a value to the existing value of the
agent. ‘Value’ is the value passed to the function which in turn will be used to update the
value of the agent accordingly.
Return Value - First returns the agent as it is, if there is a non-blocking function. In the
end, returns an agent object with a new value.
154
Clojure
(ns [Link]
(:gen-class))
(defn Example []
(def counter (agent 0))
(println @counter)
(send-off counter + 100)
(println @counter)
(println @counter))
(Example)
We are looking at the same example of incrementing the value of the counter, but from
the following output it will be clear what the send-off function does.
0
0
0
It can be seen that even though we have sent the agent a function to set the value to 100,
it does not reflect immediately. The send-off function first returns the value of the agent
as it is. Once the value of the agent has been set properly by Clojure, the value of the
agent is then updated and we are able to see the new value of the agent.
await-for
Since there is a delay when a value of an agent is updated, Clojure provided a ‘await-for’
function which is used to specify time in milliseconds to wait for the agent to be updated.
Parameters – ‘agentname’ is the agent for which the ‘await-for’ function should be set
to. ‘time’ is the time in milliseconds to wait.
Return Value - Returns logical false if returning due to timeout, otherwise returns logical
true.
155
Clojure
(ns [Link]
(:gen-class))
(defn Example []
(def counter (agent 0))
(println @counter)
(send-off counter + 100)
(println (await-for 100 counter))
(println @counter)
(shutdown-agents))
(Example)
0
true
100
You can see from the above program that the value of the agent is printed to the screen
immediately because the ‘await-for’ function incorporated a delay, which allowed Clojure
to update the value of the agent.
await
Blocks the current thread (indefinitely!) until all actions dispatched thus far, from this
thread or agent, to the agent(s) have occurred. Will block on failed agents.
(await agentname)
Parameters – ‘agentname’ is the agent for which the await function should be set to.
(ns [Link]
(:gen-class))
(defn Example []
(def counter (agent 0))
(println @counter)
156
Clojure
0
100
You can see from the above program that the value of the agent is printed to the screen
immediately because the await function will wait for Clojure to first update the value of
the function and only then will return control to the calling program.
agent-error
Returns the exception thrown during an asynchronous action of the agent, if the agent
fails. Returns nil if the agent does not fail.
(agent-error agentname)
Parameters – ‘agentname’ is the agent for which the agent-error function should be set
to.
Return Value - Returns the exception thrown during an asynchronous action of the agent
if the agent fails. Returns nil if the agent does not fail.
(ns [Link]
(:gen-class))
(defn Example []
(def my-date (agent([Link].)))
(send my-date + 100)
(await-for 100 my-date)
(println (agent-error my-date))
)
(Example)
157
Clojure
In the above program we are forcefully causing an exception to occur by incrementing the
value of a date variable which is wrong. This will cause an exception and with the help of
the ‘prinltn’ statement, will be sent to the screen.
158
30. Clojure –Watchers Clojure
Watchers are functions added to variable types such as atoms and reference variables
which get invoked when a value of the variable type changes. For example, if the calling
program changes the value of an atom variable, and if a watcher function is attached to
the atom variable, the function will be invoked as soon as the value of the atom is changed.
add-watch
Adds a watch function to an agent/atom/var/ref reference. The watch ‘fn’ must be a ‘fn’
of 4 args: a key, the reference, its old-state, its new-state. Whenever the reference's state
might have been changed, any registered watches will have their functions called.
(ns [Link]
(:gen-class))
(defn Example []
(def x (atom 0))
(add-watch x :watcher
(fn [key atom old-state new-state]
(println "The value of the atom has been changed")
(println "old-state" old-state)
(println "new-state" new-state)))
(reset! x 2)
)
(Example)
159
Clojure
remove-watch
Removes a watch which has been attached to a reference variable.
Parameters – ‘variable’ is the name of the atom or reference variable. ‘watchname’ is the
name given to the watch when the watch function is defined.
(ns [Link]
(:gen-class))
(defn Example []
(def x (atom 0))
(add-watch x :watcher
(fn [key atom old-state new-state]
(println "The value of the atom has been changed")
(println "old-state" old-state)
(println "new-state" new-state)))
(reset! x 2)
(remove-watch x :watcher)
(reset! x 4) )
(Example)
You can clearly see from the above program that the second reset command does not
trigger the watcher since it was removed from the watcher’s list.
160
31. Clojure - Macros Clojure
In any language, Macros are used to generate inline code. Clojure is no exception and
provides simple macro facilities for developers. Macros are used to write code-generation
routines, which provide the developer a powerful way to tailor the language to the needs
of the developer.
defmacro
This function is used to define your macro. The macro will have a macro name, a parameter
list and the body of the macro.
Parameters – ‘name’ is the name of the macro. ‘params’ are the parameters assigned to
the macro. ‘body’ is the body of the macro.
(ns [Link]
(:gen-class))
(defn Example []
(defmacro Simple []
(println "Hello"))
(macroexpand '(Simple))
)
(Example)
Hello
From the above program you can see that the macro ‘Simple’ is expanded inline to ‘println’
“Hello”. Macros are similar to functions, with the only difference that the arguments to a
form are evaluated in the case of macros.
161
Clojure
macro-expand
This is used to expand a macro and place the code inline in the program.
(macroexpand macroname)
(ns [Link]
(:gen-class))
(defn Example []
(defmacro Simple []
(println "Hello"))
(macroexpand '(Simple))
)
(Example)
Hello
162
Clojure
(ns [Link]
(:gen-class))
(defn Example []
(defmacro Simple [arg]
(list 2 arg))
(println (macroexpand '(Simple 2)))
)(Example)
The above example places an argument in the Simple macro and then uses the argument
to add argument value to a list.
(2 2)
163
32. Clojure - Reference Values Clojure
Reference values are another way Clojure can work with the demand to have mutable
variables. Clojure provides mutable data types such as atoms, agents, and reference
types. They mostly all work in the same way. This chapter focuses on how reference types
can be used.
ref
This is used to create a reference value. When creating a reference value, there is an
option to provide a validator function, which will validate the value created.
(ref x options)
Parameters – ‘x’ is the value which needs to be provided to the reference. ‘Options’ is a
set of options that can be provided, such as the validate command.
(ns [Link]
(:gen-class))
(defn Example []
(def my-ref (ref 1 :validator pos?))
(println @my-ref)
)
(Example)
To access the value of the reference value, you can use the @ symbol.
164
Clojure
ref-set
This function is used to set the value of a reference to a new value irrespective of whatever
is the older value.
Parameters – ‘refname’ is the name of the variable holding the reference value.
‘newvalue’ is the new value that needs to be associated with the reference type.
(ns [Link]
(:gen-class))
(defn Example []
(def my-ref (ref 1 :validator pos?))
(dosync
(ref-set my-ref 2))
(println @my-ref)
)
(Example)
alter
This function is used to alter the value of a reference type but in a safe manner. This is
run in a thread, which cannot be accessed by another process. This is why the command
needs to be associated with a ‘dosync’ method always. Secondly, to change the value of
a reference type, a function needs to be called to make the necessary change to the value.
165
Clojure
Parameters – ‘refname’ is the name of the variable holding the reference value. ‘fun’ is
the function which is used to change the value of the reference type.
(ns [Link]
(:gen-class))
(defn Example []
(def names (ref []))
(defn change [newname]
(dosync
(alter names conj newname)))
(change "John")
(change "Mark")
(println @names)
)
(Example)
[John Mark]
dosync
Runs the expression (in an implicit do) in a transaction that encompasses expression and
any nested calls. Starts a transaction if none is already running on this thread. Any
uncaught exception will abort the transaction and flow out of dosync.
(dosync expression)
Parameters – ‘expression’ is the set of expressions, which will come in the dosync block.
166
Clojure
(ns [Link]
(:gen-class))
(defn Example []
(def names (ref []))
(defn change [newname]
(dosync
(alter names conj newname)))
(change "John")
(change "Mark")
(println @names)
)
(Example)
[John Mark]
commute
Commute is also used to change the value of a reference type just like alter and ref-set.
The only difference is that this also needs to be placed inside a ‘dosync’ block. However,
this can be used whenever there is no need to know which calling process actually changed
the value of the reference type. Commute also uses a function to change the value of the
reference variable.
Parameters – ‘refname’ is the name of the variable holding the reference value. ‘fun’ is
the function which is used to change the value of the reference type.
167
Clojure
(ns [Link]
(:gen-class))
(defn Example []
(def counter (ref 0))
(defn change [counter]
(dosync
(commute counter inc)))
(change counter)
(println @counter)
(change counter)
(println @counter)
)
(Example)
1
2
168
33. Clojure - Databases Clojure
In order to use the database functionality, please ensure to first download the jdbc files
from the following url - [Link]
You will find a zip file which has the necessary drivers for Clojure to have the ability to
connect to databases. Once the zip file is extracted, ensure to add the unzipped location
to your classpath.
The main file for database connectivity is a file called [Link] in the location clojure/java.
The clojure jdbc connector supports a wide variety of databases, some of which are the
following.
H2Database
Oracle
MySQL
PostgreSQL
Database Connection
Before connecting to a MySQL database, make sure of the following:
This table has fields FIRST_NAME, LAST_NAME, AGE, SEX and INCOME.
Ensure you have downloaded the ‘mysql jar file’ and added the file to your
classpath.
(def connection_name
{ :subprotocol “protocol_name”
:subname “Location of mysql DB”
:user “username” :password “password” })
169
Clojure
Return Value - This will provide a connection string, which can be used in subsequent
mysql operations.
The following example shows how to connect to the tables in the information schema and
retrieve all the data in the table.
(ns [Link]
(:require [[Link] :as sql]))
(defn -main []
(def mysql-db {:subprotocol "mysql"
:subname "//[Link]:3306/information_schema"
:user "root"
:password "shakinstev"})
(println (sql/query mysql-db
["select table_name from tables"]
:row-fn :table_name)
))
Querying Data
Querying data on any database means to fetch some useful information from the database.
Once a database connection is established, you are ready to make a query into this
database. Following is the syntax by which data can be queried using Clojure.
[Link]/query dbconn
["query"]
:row-fn :sequence
Parameters – ‘dbconn’ is the name of the connection used to connect to the database.
‘query’ is the query string used to fetch data from the database. ‘:sequence’ is by default
all the rows of data fetched from the database and is returned as a sequence. The
necessary operations on the sequence can then be done to see what data has been
fetched.
Return Value - This will return a sequence, which will have the rows of data from the
query operation.
170
Clojure
The following example shows how to connect to the employee table and fetch the
first_name column of the rows in the table.
(ns [Link]
(:require [[Link] :as sql]))
(defn -main []
(def mysql-db {:subprotocol "mysql"
:subname "//[Link]:3306/testdb"
:user "root"
:password "shakinstev"})
(println (sql/query mysql-db
["select first_name from employee"]
:row-fn :first_name)
))
The query of “select first_name from employee” is passed as the query string.
If we assume that there is just one row in our database which contains a first_name value
of John, following will be the output of the above program.
(John)
Inserting Data
It is required when you want to create your records into a database table. Following is the
syntax by which data can be inserted using Clojure. This is done by using the ‘insert!’
function.
[Link]/insert!
:table_name {:column_namen columnvalue}
Parameters – ‘:table_name’ is the name of the table in which the insertion needs to be
made. ‘{:column_namen columnvalue }’ is a map of all the column names and values,
which need to be added as a row in the table.
Return Value - This will return nil if the insertion is made successfully.
171
Clojure
The following example shows how to insert a record into the employee table in the testdb
database.
(ns [Link]
(:require [[Link] :as sql]))
(defn -main []
(def mysql-db {:subprotocol "mysql"
:subname "//[Link]:3306/testdb"
:user "root"
:password "shakinstev"})
(sql/insert! mysql-db
:employee {:first_name "John" :last_name "Mark" :sex "M" :age 30 :income 30})
)
If you now check your MySQL database and the employee table, you will see that the
above row will be successfully inserted in the table.
Deleting Data
Rows can be deleted from a table by using the ‘delete!’ function. Following is the syntax
on how this operation can be performed.
[Link]/delete!
:table_name [condition]
Parameters – ‘:table_name’ is the name of the table in which the insertion needs to be
made. ‘condition’ is the condition used to determine which row needs to be deleted from
the table.
The following example shows how to delete a record from the employee table in the testdb
database. The example deletes a row from the table based on the condition that the age
is equal to 30.
(ns [Link]
(:require [[Link] :as sql]))
(defn -main []
(def mysql-db {:subprotocol "mysql"
:subname "//[Link]:3306/testdb"
:user "root"
:password "shakinstev"})
172
Clojure
If you had a record which had a row with age equal to the value of 30, that row will be
deleted.
Updating Data
Rows can be updated from a table by using the ‘update!’ function. Following is the syntax
on how this operation can be performed.
[Link]/update!
:table_name
{setcondition}
[condition]
Parameters – ‘:table_name’ is the name of the table in which the insertion needs to be
made. ‘setcondition’ is the column which needs to be updated as mentioned in terms of a
map. ‘condition’ is the condition which is used to determine which row needs to be deleted
from the table.
The following example shows how to delete a record from the employee table in the testdb
database. The example updates a row from the table based on the condition that the age
is equal to 30 and updates the value of income to 40.
(ns [Link]
(:require [[Link] :as sql]))
(defn -main []
(def mysql-db {:subprotocol "mysql"
:subname "//[Link]:3306/testdb"
:user "root"
:password "shakinstev"})
(println (sql/update! mysql-db
:employee
{:income 40}
["age = ? " 30])
))
173
Clojure
If you had a record which had a row with age equal to the value of 30, that row will be
updated wherein the value of income will be set to 40.
Transactions
Transactions are mechanisms that ensure data consistency. Transactions have the
following four properties:
Consistency: A transaction must start in a consistent state and leave the system
in a consistent state.
Isolation: Intermediate results of a transaction are not visible outside the current
transaction.
Durability: Once a transaction was committed, the effects are persistent, even
after a system failure.
The following example shows how to implement transactions in Clojure. Any operations
which needs to be performed in a transaction needs to be embedded in the ‘with-db-
transaction’ clause.
(ns [Link]
(:require [[Link] :as sql]))
(defn -main []
(def mysql-db {:subprotocol "mysql"
:subname "//[Link]:3306/testdb"
:user "root"
:password "shakinstev"})
(sql/with-db-transaction [t-con mysql-db]
(sql/update! t-con
:employee
{:income 40}
["age = ? " 30]))
)
174
34. Clojure - Java Interface Clojure
As we already know, Clojure code runs on the Java virtual environment at the end. Thus
it only makes sense that Clojure is able to utilize all of the functionalities from Java. In
this chapter, let’s discuss the correlation between Clojure and Java.
(ns Project
(:gen-class))
(defn Example []
(println (.toUpperCase "Hello World")))
(Example)
The above program produces the following output. You can see from the code that if you
just call the dot notation for any string method, it will also work in Clojure.
HELLO WORLD
(ns Project
(:gen-class))
(defn Example []
(println (.indexOf "Hello World","e"))
)
(Example)
175
Clojure
The above program produces the following output. You can see from the above code, that
we are passing the parameter “e” to the indexOf method. The above program produces
the following output.
(ns Project
(:gen-class))
(defn Example []
(def str1 (new String "Hello"))
(println str1)
)
(Example)
The above program produces the following output. You can see from the above code, that
we can use the ‘new’ keyword to create a new object from the existing String class from
Java. We can pass the value while creating the object, just like we do in Java. The above
program produces the following output.
Hello
Following is another example which shows how we can create an object of the Integer
class and use them in the normal Clojure commands.
(ns Project
(:gen-class))
(defn Example []
(def my-int(new Integer 1))
(println (+ 2 my-int))
)
(Example)
176
Clojure
Import Command
We can also use the import command to include Java libraries in the namespace so that
the classes and methods can be accessed easily.
The following example shows how we can use the import command. In the example we
are using the import command to import the classes from the [Link] library. We
can then use the push and pop method of the stack class as they are.
(ns Project
(:gen-class))
(import [Link])
(defn Example []
(let [stack (Stack.)]
(.push stack "First Element")
(.push stack "Second Element")
(println (first stack))))
(Example)
First Element
You have to mention the Clojure jar file, so that all Clojure-based classes will be loaded in
the JVM. The ‘[Link]’ file is the Clojure code file which needs to be executed.
177
Clojure
Math PI function - Clojure can use the Math method to the value of PI. Following is an
example code.
(ns Project
(:gen-class))
(defn Example []
(println (. Math PI))
)
(Example)
3.141592653589793
System Properties - Clojure can also query the system properties. Following is an
example code.
(ns Project
(:gen-class))
(defn Example []
(println (.. System getProperties (get "[Link]")))
)
(Example)
Depending on the version of Java on the system, the corresponding value will be displayed.
Following is an example output.
1.7.0_79
178
35. Clojure - Concurrent Programming Clojure
In Clojure programming most data types are immutable, thus when it comes to concurrent
programming, the code using these data types are pretty safe when the code runs on
multiple processors. But many a times, there is a requirement to share data, and when it
comes to shared data across multiple processors, it becomes necessary to ensure that the
state of the data is maintained in terms of integrity when working with multiple processors.
This is known as concurrent programming and Clojure provides support for such
programming.
The software transactional memory system (STM), exposed through dosync, ref, set, alter,
etc. supports sharing changing state between threads in a synchronous and coordinated
manner. The agent system supports sharing changing state between threads in an
asynchronous and independent manner. The atoms system supports sharing changing
state between threads in a synchronous and independent manner. Whereas the dynamic
var system, exposed through def, binding, etc. supports isolating changing state within
threads.
Other programming languages also follow the model for concurrent programming.
If shared access is required, the object is locked, the value is changed, and the
process continues for the next access to that value.
In Clojure there are no locks, but Indirect references to immutable persistent data
structures.
The following operations are possible in Clojure with regards to concurrent programming.
Transactions
Concurrency in Clojure is based on transactions. References can only be changed within a
transaction. Following rules are applied in transactions.
We already seen what the dosync block does, let’s look at it again.
179
Clojure
dosync
Runs the expression (in an implicit do) in a transaction that encompasses expression and
any nested calls. Starts a transaction if none is already running on this thread. Any
uncaught exception will abort the transaction and flow out of dosync.
(dosync expression)
Parameters – ‘expression’ is the set of expressions which will come in the dosync block.
Let’s look at an example wherein we try to change the value of a reference variable.
(ns [Link]
(:gen-class))
(defn Example []
(def names (ref []))
(alter names conj "Mark")
)
(Example)
From the error you can clearly see that you cannot change the value of a reference type
without first initiating a transaction.
180
Clojure
In order for the above code to work, we have to place the alter command in a dosync block
as done in the following program.
(ns [Link]
(:gen-class))
(defn Example []
(def names (ref []))
(defn change [newname]
(dosync
(alter names conj newname)))
(change "John")
(change "Mark")
(println @names)
)
(Example)
[John Mark]
(ns [Link]
(:gen-class))
(defn Example []
(def var1 (ref 10))
(def var2 (ref 20))
(println @var1 @var2)
In the above example, we have two values which are being changed in a dosync block. If
the transaction is successful, both values will change else the whole transaction will fail.
10 20
-10 40
182
36. Clojure - Applications Clojure
Clojure has some contributed libraries which have the enablement for creating Desktop
and Web-based applications. Let’s discuss each one of them.
Desktop – See-saw
See-saw is a library which can be used for creating desktop applications. In order to use
See-saw, first download the .clj file from the following github link
[Link]
Then create a sample desktop application. Following is the code for the same.
(ns [Link]
(:gen-class)
(:require [[Link] :as seesaw]))
(def window (seesaw/frame
:title "First Example"
:content "hello world"
:width 200
:height 50))
(defn -main
[& args]
(seesaw/show! window))
When the above code is run, you will get the following window.
183
Clojure
First you need to ensure you use the [Link] library so all of the available
methods can be used.
The attributes of frame and content can be used to define the title and what content
needs to be shown in the window.
(ns [Link]
(:gen-class)
(:require [[Link] :as seesaw]))
(def window (seesaw/frame
:title "First Example"
:content "hello world"
:width 200
:height 50))
(defn -main
[& args]
(seesaw/show! window)
(seesaw/config! window :content "Good Bye"))
When the above code is run, you will get the following window.
184
Clojure
(ns [Link]
(:gen-class)
(:require [[Link] :as seesaw]))
(def window (seesaw/frame
:title "First Example"
:content "hello world"
:width 200
:height 50))
(defn -main
[& args]
(seesaw/show! window)
(seesaw/alert "Hello World"))
When the above code is run, you will get the following window.
185
Clojure
(ns [Link]
(:gen-class)
(:require [[Link] :as seesaw]))
(defn -main [& args]
(defn display
[content]
(let [window (seesaw/frame :title "Example")]
(-> window
(seesaw/config! :content content)
(seesaw/pack!)
(seesaw/show!))))
(def button
(seesaw/button
:text "Click Me"
:listen [:action (fn [event](seesaw/alert "Click!" ))]))
(display button))
In the above code, first a button variable is created which is from the button class of the
see-saw library. Next, the text of the button is set to “Click Me”. Then an event is attached
to the button so that whenever the button is clicked, it will show an alert dialog box.
When the above code is run, you will get the following window.
When you click the button, you will get the following dialog box.
186
Clojure
(ns [Link]
(:gen-class)
(:require [[Link] :as seesaw]))
(defn -main [& args]
(defn display
[content]
(let [window (seesaw/frame :title "Example")]
(-> window
(seesaw/config! :content content)
(seesaw/pack!)
(seesaw/show!))))
(def label (seesaw/label
:text "This is a label too"
:background :white
:foreground :black
:font "ARIAL-BOLD-10"))
(display label))
In the above code, first a label variable is created which is from the label class of the
seesaw library. Next, the text of the label is set to "This is a label too". Then, the
background, foreground color, and font are set accordingly.
When the above code is run, you will get the following window.
187
Clojure
(ns [Link]
(:gen-class)
(:require [[Link] :as seesaw]))
(defn -main [& args]
(defn display
[content]
(let [window (seesaw/frame :title "Example")]
(-> window
(seesaw/config! :content content)
(seesaw/pack!)
(seesaw/show!))))
(def textfield
(seesaw/text
:text "This is a text field"
:editable? false
:columns 50))
(display textfield))
In the above code, first a text field variable is created which is from the text class of the
seesaw library. Next, the text of the text field is set to "This is a text field". Then the text
field is made a static field by setting the editable attribute to false.
When the above code is run, you will get the following window.
You need to ensure you download the necessary jars from the site and ensure to add it as
a dependency for the Clojure application.
188
Clojure
Sets things up such that an http request comes into your web application as a
regular Clojure HashMap, and likewise makes it so that you can return a response
as a HashMap.
Provides a specification describing exactly what those request and response maps
should look like.
Brings along a web server (Jetty) and connects your web application to it.
The Ring framework automatically can start a web server and ensures the Clojure
application works on this server. Then one can also use the Compojure framework. This
allows one to create routes which is now how most modern web applications are
developed.
Creating your first Clojure application - The following example shows how you can
create your first web application in Clojure.
(ns [Link]
(:require [[Link] :refer :all]
[[Link] :as route]
[[Link] :refer [wrap-defaults site-defaults]]))
(defroutes app-routes
(GET "/" [] "Hello World")
(route/not-found "Not Found"))
(def app
(wrap-defaults app-routes site-defaults))
The ‘defroutes’ is used to create routes so that request made to the web
application to different routes can be directed to different functions in your Clojure
application.
In the above example, the “/” is known as the default route, so when you browse
to the base of your web application, the string “Hello World” will be sent to the web
browser.
If the user hits any url which cannot be processed by the Clojure application, then
it will display the string “Not Found”.
189
Clojure
When you run the Clojure application, by default your application will be loaded as
localhost:3000, so if you browse to this location, you will receive the following output.
(ns [Link]
(:require [[Link] :refer :all]
[[Link] :as route]
[[Link] :refer [wrap-defaults site-defaults]]))
(defroutes app-routes
(GET "/" [] "Hello World")
(GET "/Tutorial" [] "This is a tutorial on Clojure")
(route/not-found "Not Found"))
(def app
(wrap-defaults app-routes site-defaults))
You can see that adding a route in the application is as easy as just adding another GET
function with the url route. (GET "/Tutorial" [] "This is a tutorial on Clojure")
190
Clojure
If you browse to the location [Link] you will receive the following
output.
191
37. Clojure - Automated Testing Clojure
This URL provides the speclj framework, which is used as a Test data driven or Behaviour
driven test framework for Clojure. You have to ensure that you use the Clojure 1.7.0
framework when using any of the ‘speclj’ libraries. By default, the test files will be different
from the Clojure code files and need to be placed in a ‘spec’ directory.
(ns [Link]-spec
(:require [[Link] :refer :all]
))
(describe "Truth"
(run-specs)
We first have to ensure to use the ‘require’ statement to include all the core libraries
in the ‘speclj’ framework.
Next is the ‘describe’ function. This is used to provide a description for the test case
being created.
Next function is the ‘it’ function, which is the actual test case. In the first test case,
the “is true” string is the name given to the test case.
192
Clojure
Should and should-not are known as assertions. All assertions begin with should.
Should and should-not are just two of the many assertions available. They both
take expressions that they will check for truthy-ness and falsy-ness respectively.
If you run the test case, you will get the following output. The output shows the time taken
in milliseconds for the test case to run.
←[32m.←[0m←[32m.←[0m
Let’s look at how we can use the Selenium libraries for testing Clojure web-based
applications.
Step 1: The first step is to ensure we are using the Ring and Compojure framework to
create a web-based application, which needs to be tested. Let’s use one of the examples
from our earlier chapters. The following code is a simple web application, which displays
“Hello World” in the browser.
(ns [Link]
(:require [[Link] :refer :all]
[[Link] :as route]
[[Link] :refer [wrap-defaults site-defaults]]))
(defroutes app-routes
(GET "/" [] "Hello World")
(route/not-found "Not Found"))
(def app
(wrap-defaults app-routes site-defaults))
Step 3: Also ensure to download the ‘clj’ web driver, which will be used for running the
web test from the following location.
[Link]
193
Clojure
Step 4: In your project directory, create another directory called features and create a file
called ‘[Link]’.
Step 5: Next add the following code to the ‘[Link]’ file created in the earlier step.
ns [Link])
(def test-port 3000)
(def test-host "localhost")
(def test-base-url (str "[Link] test-host ":" test-port "/"))
The above code basically tells the web test framework to test the application, which gets
loaded at the URL [Link]
Step 6: Finally, let’s write our code to carry out our test.
(ns [Link]
(:require [[Link] :refer :all]
[[Link] :refer [run-jetty]]
[[Link] :refer :all]
[[Link] :refer :all]
[[Link] :refer [app-routes]]))
(ns [Link]
(:require [[Link] :refer :all]
[[Link] :refer [run-jetty]]
[[Link] :refer :all]
[[Link] :refer :all]
[[Link] :refer [app-routes]]))
(defn start-server []
(loop [server (run-jetty app-routes {:port test-port, :join? false})]
(if (.isStarted server)
server
(recur server))))
(defn start-browser []
(set-driver! {:browser :firefox}))
194
Clojure
(defn stop-browser []
(quit))
(deftest homepage-greeting
(let [server (start-server)]
(start-browser)
(to test-base-url)
(is (= (text "body") "Hello World"))
(stop-browser)
(stop-server server)))
195
38. Clojure - Libraries Clojure
One thing which makes the Clojure library so powerful is the number of libraries available
for the Clojure framework. We have already seen so many libraries used in our earlier
examples for web testing, web development, developing swing-based applications, the
jdbc library for connecting to MySQL databases. Following are just a couple of examples
of few more libraries.
[Link]
This library allows Clojure to work with XML data. The library version to be used is
[Link]/[Link] "0.0.8". The [Link] supports parsing and emitting XML. The
parsing functions will read XML from a Reader or InputStream.
(ns [Link]
(use '[Link])
(:gen-class))
(defn Example []
(let [input-xml ([Link]. "<?xml version=\"1.0\"
encoding=\"UTF-8\"?><example><clo><Tutorial>The Tutorial
value</Tutorial></clo></example>")]
(parse input-xml))
#[Link]{:tag :example, :attrs {},
:content (#[Link]{:tag :clo,:attrs {},
:content (#[Link]{:tag :Tutorial,
:attrs {},:content ("The Tutorial value")})})}
(Example)
[Link]
This library allows Clojure to work with JSON data. The library version to be used is
[Link]/[Link] "0.2.6".
(ns [Link]
(:require [[Link] :as json])
(:gen-class))
(defn Example []
(println (json/write-str {:a 1 :b 2})) (Example)
196
Clojure
{\"a\":1,\"b\":2}
[Link]
This library allows Clojure to work with ‘csv’ data. The library version to be used is
[Link]/[Link] "0.1.3".
(ns [Link]
(require '[[Link] :as csv]
'[[Link] :as io])
(:gen-class))
(defn Example []
(with-open [in-file (io/reader "[Link]")]
(doall
(csv/read-csv in-file)))
(with-open [out-file (io/writer "[Link]")]
(csv/write-csv out-file
[[":A" "a"]
[":B" "b"]]))
(Example)
In the above code, the ‘csv’ function will first read a file called [Link] and put all the
data in the variable in-file. Next, we are using the write-csv function to write all data to a
file called [Link].
197