Clojure Tutorial I

joydeep bhattacharjee
Technology at Nineleaps
5 min readJun 26, 2017

1. Getting Started

1.1. Running the Clojure Interpreter

Clojure is powerful, easy to use, general purpose programming language that runs on the JVM. Clojure like other Lisp dialects is dynamic, which means that it is not overly typed based system and gives you some room to breathe. In addition to that Clojure is compiled and can easily call upon the vast amount of Java libraries and the goodness of the JVM. I have created this series keeping in mind the average programmer who is more experienced in C-based programming languages like Java, Python, JavaScript. If you come from such programming languages and are looking into exploring the world of a full-fledged functional programming language, then Clojure is the one for you.

This chapter is the starting of a series on Clojure. We will try to see the internals of Clojure and at the end of it seek to build a real world app.
You can run Clojure both as a jar file or as an interpreter. We will look at how to distribute the Clojure code as a jar file later on. Right now lets first start Clojure as an interpreter. To do that install leiningen which is considered to be the easiest way to use Clojure.

First, you need to install Java if you don’t have that installed already. The below instructions are for an Ubuntu machine. In case you have a different machine and are not able to install the dependencies, write to me and we will try to find what the issue might be.

You can then follow the installation steps as shown below. This is the same as the official installation steps which I followed in my Ubuntu machine.

Once done you can start the lein repl command and this will download the necessary libraries and start a repl for you.

Now check if you can run the code and if it gives the correct result.

user=> (+ 1 1)
2

Voila! You have successfully started your Clojure interpreter and have run a Clojure program.

1.2 Assignments

The use of global variables is recommended to avoid in Clojure and to keep the functions as clean and pure as possible. But you can create a persistent reference to a changing object. This is done through the use of Vars.

Vars are generally static. So, if you need to make it dynamic and have some calculations based on them, you will need to use the macro binding. Bindings are thread-local and do not propagate outside the thread.

1.3 Numbers

Clojure is a dialect of Lisp and that means that means that operators are considered as functions and so come before the arguments that should go into the operation.

1.4 Strings

Strings are believed to be one of the basic building blocks of programming. Almost everything that you do will involve manipulation of strings.In Clojure, a string literal is created by enclosing the string in double quotes.

To concatenate some string variables you can use the `str` function:

You can also use string formatting using the Format function.

And then join them back together.

1.5 Functions

Clojure is a functional programming language. So you will be creating a lot of functions. Functions that do something, functions that pass functions, functions that manipulate other functions and so on. You can consider a function to be unrealized functionality. Generally, functions are defined in the following manner.

(defn name doc-string? attr-map? [params*] body)

For example, a function that returns a square of a number can be created.

The above code should return 4. Now the functions can be used in other expressions.

(+ (my_square 2) (my_square 3))

which should give the output 13.

If you just need to create a small function that you can consider writing anonymous functions usingfn .

user=> ((fn [x] (* x x)) 2)
4

1.6 Conditions

One way of implementing branching in Clojure is through the if statement which is basically a test. The format is basically:

(if condition-statment <if condition is true return this> <(optional)if condition is false return this>)

The above construct will be clear in the below examples.

user=> (if nil "truth value" "false value")
"false value"

In Clojure, only nil and false are considered logical false.

user=> (if "true" "truth value" "false value")
"truth value"

But if cannot take multiple tests in the format of if..elif..else. For that you will need to use thecond macro. Then you can pass multiple test parameters.

The above constructs are more in line with the imperative languages.Of course, you can convert any if constructs to filter, but more on that later.

1.7 Data Containers and Data Structures.

Clojure has four primary data structure or collections of data.

  1. Lists
  2. Vectors
  3. Maps
  4. Sets

Lists in Clojure are singly linked lists. So modifications of lists are efficient while random access is not. Lists are one of the most important data structures in Clojure. A code is data in Clojure and they are represented in lists.
You can either use the list function or the quote literal to creating a list.

user=> (list "clojure" "F#" "python" "java")
("clojure" "F#" "python" "java")
user=> '("clojure" "F#" "python" "java")
("clojure" "F#" "python" "java")

Get the first or nth element of the list.

You can treat the list like a stack with peek and pop.

You can also extend a list to a new list with conj and concat. Notice that the conjoining in lists happen at the beginning of the list.

B. Vectors

Vectors are similar to arrays in C and Java. The elements in a vector are indexed by contiguous integers. Vectors are optimized for random lookups.

To create a vector you can use the vector function or just define them.

Retrieving the nth element of the vector is optimised for a vector.

Concatenation works the same way to lists but conjoining happens at the end of the vector.

C. Maps or HashMaps.

Maps are equivalent to dictionaries in Python and HashMaps in Java. Maps associate keys to values and the keys must be comparable. There are numerous implementations of maps with various guarantees of ordering and different performance on the lookups.

You can create a map using literals.

user=> (def fav-filmmakers {:stanley-kubrik "26 July 1928"
#_=> :satyajit-ray "2 May 1921"})
#'user/fav-filmmakers
user=>

Note that keywords are used as keys here. This is more of a convention and not really required. To access the value corresponding to a particular key, you can use the get function or use the key specified as a function.

D. Sets

Sets are similar to mathematical sets and are a collection of unique values. They are hashed and unordered.
To create a set you can use a literal or the set function.

user=> #{1 2 3 4 5}
#{1 4 3 2 5}
user=> (set [1 2 3 4 5])
#{1 4 3 2 5}

Sets are collections so you can count, conjugate them with other elements or break them or see if they contain some element.

As you can expect from the name, sets support set-operations like union, difference, intersection, etc. Set operations require the set namespace to be loaded.

Note:

DataStructures are functions:

  • Maps are functions of their keys
  • Keywords are functions of maps
  • Sets are functions of their elements
  • Vectors are functions of their indices

Hope you liked this!
To get regular updates on more content, learning material and general rants you can follow me on Twitter.

--

--