Introduction to Clojure

Clojure is a modern dialect of Lisp designed to run on the Java Virtual Machine and the Common Language Runtime.  Descending from Lisp, Clojure has a strong emphasis on functional programming and a philosophy of treating code as data.  Learning Lisp will challenge you to think about programs and programming in a new way from procedure oriented languages.  Clojure is a very accessible dialect of Lisp that runs on most computers.

This gentle introduction assumes that you are coming from a procedural language language like C or Java with little or no exposure to Lisp.

The REPL

If you’re approaching Clojure from a compiled language like C or Java, the Clojure REPL might be an unfamiliar way of working with a language.  REPL stands for Read Evaluate Print Loop is an interactive programming environment which allows you to develop Clojure code one expression at a time.  We’ll start demonstrating Clojure through the Clojure REPL tool clj.

To run our sample code, you will need to install Clojure locally or you can run it from a online interpreter.  Instructions for installing Clojure locally can be found on the Getting Started page at Clojure.org.  If you would prefer to start with an online interpreter, you can try repl.it or any of the  other sites that come up following a search.

Assuming that you are running your code locally, launch clj from the command line.  You should see a prompt similar to:

Clojure 1.9.0
user=>

The prompt tells us we are running version 1.9.0 of Clojure and that you can now start typing expressions for the interpreter to evaluate.  (You can press Control + D at any time to quit.) We’ll start by typing in some basic mathematical expressions in Clojure.

Clojure Expressions

user=> (+ 1 3 5)
9
user=> (* 2 4)
8
user=> (/ 4 2 2)
1

Our basic math expressions consist of a prefix operator and a dynamic number of argument.  At first glance, this might look like some simple syntactic difference between Clojure and C-like languages, but the differences run a bit deeper.  In C-like languages, programs consist of statements and expressions. Statements have side-effects but don’t have a value, only expressions have a value.  Clojure programs are built entirely out of expressions. Clojure expressions consist of a list (marked by parenthesis) consisting of a function and a series of 0 or more arguments.

More complex expressions can be built up by combining expressions into larger expressions:

user=> (+ (/ 4 2) (* 3 2) (+ 1 3 7))
19

In Clojure, the primary data structure used in the language is lists.  We’ve already seen the use of lists to construct expressions, we can also store data in lists and manipulate our data using functions:

user=> (first '(1 2 3 4 5))
1
user=> (rest '(1 2 3 4 5))
(2 3 4 5)

In the two expression above, we treat our list as a data object by using quote (').   Quote is a function that returns the unevaluated form of the list – the interpreter doesn’t try to evaluate 1 as a function.  Here we use the function first to get the first element of the list and rest to get the remainder (as a list).

Function Definition and Special Forms
Clojure allows you to create a function using the defn special form.  You’ve already seen one example of a special form (quote), defn is another.  A special form is an expression that has its own evaluation rule.  For now, you can think of special forms as the built-in syntax of the language that you can use for developing programs.  Following ancient programmer tradition, let’s create a hello world function in Clojure:

user=> (defn hello-world [] (print "Hello World!\n"))
#'user/hello-world

Our function definition is event an expression that evaluates to a value.  It starts with the name (hello-world) followed by a list of arguments (in this case none) and an expression to perform our computation.  In this case, which is slightly unusual, our expression has a side effect which is to print the message “Hello World!”.

Now, let’s create a function add-one that increments a value:

user=> (defn add-one [x] (+ 1 x))
#'user/add-one

Our expression consists of a name (add-one) , a single parameter (x) and an expression representing our computation. Notice that even defn evaluates to a value, namely the symbol for the function.  We can call our function once it’s defined in another expression:

user=> (add-one 1)
2
user=> (add-one (add-one (add-one 1)))
4

Now let’s go back and try running our hello-world function:

user=> (hello-world)
Hello World!
nil

Notice we get a slightly different result, nil.  In Clojure, every expression evaluates to a value, so if a function returns no value that is represented by nil.  Nil is the absence of value

Clojure has additional special forms that allow you to build up more complex expressions.  

The if form allows us to build expressions that return different values based on a test condition.   Clojure has a built-in function zero? that tests if a value is zero.  We can write our own version of that function:

user=> (defn my-zero? [x] (if (= x 0true false))
#'user/is-zero?
user=> (zero? 2)
false
user=> (is-zero? 0)
true

It is very important to remember that if evaluates to the first expression if the test is true and the second expression if the test is false.  Those expressions could be far more complex than just true or false.

The let special form allows us to create a local binding (similar to a local variable) of the result of an expression to a symbol:

user=> (let [x 1 
             y 2]
        (+ x y))
3

With let, we define a sequence of bindings between symbols and the result of an expression.  Notice here we use let in the context of a simple expression, you don’t necessarily have to use it within a function.

Finally, recursive expressions are one of the basic building blocks in Clojure for building repeated computations.  We can build a simple recursive factorial function:

(defn factorial [x] (if (<= x 11 (* x (factorial (- x 1)))))
#'user/factorial
user=> (factorial 3)
6

Higher Order Functions

Functions in Clojure can be passed as parameters to other functions and used to build complex expressions as well.  The built-in functions map, reduce, apply are the main functions used with functional arguments.  Each of these higher order functions operates differently:
The map function applies it’s first argument (the function) to each element of the second argument (list).  We can use the map function in conjunction with our add-one function to build a new list with each element incremented by one:

(user=> (map add-one '(1 2 3 4 5))
(1 2 3 4 5)

while the reduce function take a two argument function, applies it to the first two arguments in the list then successively applies the next argument in the list to the previous result until a single element is returned:

user=> (reduce + '(1 2 3 4 5))
15

Finally the apply function applies the named function to the parameter list:

user=> (apply add-one '(1))
2

This article touches on the basics of using Clojure – building up expressions from simple expressions, special forms, and higher order functions.  Clojure is a rich language that includes a variety of built-in data structures in addition to lists, a package system, and the ability to load and access Java code in the JVM.  For more information on working with Clojure, see the Getting Started section on Clojure.org.

Leave a Reply

avatar
  Subscribe  
Notify of