Get Started With Clojure

Get up and running with Clojure in 10 minutes.

Clojure is not a very popular language, which is a bit strange considering how powerful it is and how great it is to work with. It helps you as a programmer think about and try minimise the hardest thing to deal with... state.

The biggest benefits of Clojure is that it's dynamic and functional, which means you drop the notion of all things OOP like; Objects, Classes, interfaces and Abstract Classes. It also means you're more likely to avoid issues around state.

The goal of this article is to expose the very basics of the Clojure syntax in a way that is easy to understand.

We'll go over

Installing Clojure

Installing Clojure is dead simple, and there are great instructions on how to do it here:

Installing Leiningen

While this is not required to write Clojure apps, it sure does help, and we will use it for our project.

Leiningen can be downloaded here:

Setup IDE

Personally I use Intellij CE with the Cursive plugin. There are of course other choices, but for the sake of brevity I won't go over them.

Intellij CE can be downloaded here:
Cursive is downloaded as a plugin:

Creating a project

To quickly create a new Clojure app run:

  lein new app todo  cd todo  lein run

After running those commands you should see 'Hello, World!'

Woop! Clojure setup is DONE!

Setting up the repl

A really awesome part of Clojure is using the REPL.
This is a tool that allows you to modify and add code to your project, while maintaining state and without restarting. Once you get used to it, you'll wish every language had it.

Open the 'todo' project in Intellij

In the toolbar click "Add Configuration",
Click the plus sign and add a remote repl.
Make sure you use the Leiningen port of the project

In your command line run:

  lein repl

And when you click play next to your REPL config, you will attach to the command line REPL from IntelliJ.

Open the file 'src/todo/core.clj' and under the main function write (-main) so your core.clj file looks like this:

  (ns todo.core

  (defn -main
    "I don't do a whole lot ... yet."
    [& args]
    (println "Hello, World!"))


If you press CMD+SHIFT+P when your cursor is at the end of (-main) or if you right click next to (-main) and select
REPL -> Send (-main) to REPL The -main function will be executed inside the repl and you should see Hello, World printed out on the repl on the right

Awesome! We're running Clojure and can execute code in a REPL!

Let's make Clojure a bit easier to work with by installing Rainbow brackets, a plugin which highlights the opening and closing brackets with different colours.

Rainbow Brackets Plugin

Let's also turn Parinfer on. Its a handy tool which automatically places the closing brackets for us based on indentation. This setting can be found at the bottom right of IntelliJ's status bar.

Turn on Parinfer


Let's write some clojure! (Syntax Basics)

I want to get through the basics of Clojure as quickly as possible. So let's get straight into it.
*No fluff*

The brackets go on the outside!

Explanation of Clojure form Explanation of Clojure forms in forms

Here are some examples of Clojure's built-in functions

  (+ 1 2)
  => 3

  (+ 1 2 3)
  => 6

  (println 1 "one" nil true false)
  1 one nil true false ;; this gets printed
  => nil ;; Returns nil

  (str 1 "one" nil true false)
  => "1onetruefalse"

  (println (+ 1 2 (+ 1 2)))
  6 ;; Gets printed
  => nil ;; gets

Please note the difference between RETURNING and PRINTING

println is a function that takes any amount of arguments and prints them to the console,
but returns nil.
str is a function that takes any amount of arguments and returns them in a string but does not print them! We only see them in the REPL because the REPL automatically shows us the output of the function.

  (println 1 "one" nil true false) ;; Executed Form
  1 one nil true false ;; the console log
  => nil ;; the output

  (str 1 "one" nil true false) ;; Executed Form
  => "1onetruefalse ;; the output

Our fist function!

Lets make a function that tells the user how many letters their name has.

(defn how-long-is-your-name
    "A function that counts the letters in your name"
    (println "The name :" name "has " (count name) " letters."))

(how-long-is-your-name "Daniel")
The name : Daniel has  6  letters. ;; LOG
=> nil ;; RETURN

Explanation of function


There isn't one! The way Clojure works is that it returns the value of the last evaluated form. So this function:

(defn say-hello-lots-of-times
    (str "Hello, " name "!")
    (str "Hello, " name "!")
    (str "Hello, " name "!")
    (str "Hello, " name "!"))

(say-hello-lots-of-times "Daniel")
=> "Hello, Daniel!" ;; Just returns a single string.

Data Types

Just a reminder, this isn't a full Clojure course. Just something to whet the pallet and get you going. So I'm not covering everything about all of Clojures data types. Just the ones that I use most regularly, namely Maps and Vectors.


Maps are the main tool in your clojure dev belt: They look like this:

(def bind-map-to-this {"map-key" "map-value"
                       "map-key2" "map-value2"})

Okay, I also snuck a (def) in there.
Def binds a value to symbol. In this case it binds the map we created to the symbol 'bind-map-to-this' allowing us to use the map in multiple places.

Map takes pairs of values, the first being the key and the second being the value. You can basically pass anything as the value, a function, another map, another data type. Whatever.

Normally the key of a map is a keyword. A keyword is a data type in Clojure that looks like a symbol prefixed with a colon.


Here is a practical example of a map and retrieving data from it in multiple ways.

  (def user {:name "Daniel"
             :site ""
             :race "Orc"
             :skills {:sleeping 10
                      :cooking 1
                      :lego 7}})

  ;; Access Data using the built-in get function:
  (get user :name)
  => "Daniel"

  (get user :skills)
  => {:sleeping 10
      :cooking 1
      :lego 7}

  ;; Access data inside inner map
  (get-in user [:skills :cooking])
  => 1

  ;; when data doesn't exist, you get nil.
  (get-in user [:skills :cooking :doesnt-exist])
  => nil

  ;; Use default values
  (get user :not-in-map "DEFAULT RETURN")

  (get-in user [:skills :cooking :why-am-I-here] "DEFAULT RETURN")

KEYWORDS ARE FUNCTIONS, DAWG! You can use them to get data out of maps also!

(:name user)
=> "Daniel"

You can also add and remove elements in maps. YOU WILL NEVER ACTUALLY CHANGE THE ORIGINAL MAP that's because in Clojure: DATA IS IMMUTABLE

Here are examples:

  ;; adding data...
  (assoc user :car :fiat)
  {:name "Daniel",
   :site "",
   :race "Orc",
   :skills {:sleeping 10, :cooking 1, :lego 7},
   :car :fiat}

  ;; BUT
  (println user)
  {:name Daniel, :site, :race Orc, :skills {:sleeping 10, :cooking 1, :lego 7}}


  ;; removing data...
  (dissoc user :site)
  => {:name "Daniel", :race "Orc", :skills {:sleeping 10, :cooking 1, :lego 7}}

  ;; BUT
  (println user)
  {:name Daniel, :site, :race Orc, :skills {:sleeping 10, :cooking 1, :lego 7}}

  ;; Still contains :site!

There are many more helper functions for maps. But like I said, this tutorial is just a showcase.


Vectors are like arrays. They contain an ordered list of data. They can contain pretty much anything so they aren't bound to a particular type.
They are also IMMUTABLE meaning you can't change them, you can make new ones, but the original won't change.
Here is an example of creating and manipulating a vector.

; Define a vector of cars (notice type doesn't matter)
(def cars [:fiat :bmw "mercedes" :honda])
=> #'todo.core/cars

; Add to the end of the vector
(conj cars "ford")
=> [:fiat :bmw "mercedes" :honda "ford"]

; Built in function 'first' gets the first element of the vector
(first cars)
=> :fiat

; Built in function 'rest' gets everything but the first element of the vector
(rest cars)
=> (:bmw "mercedes" :honda)

; Ok We haven't covered this syntax, but read on...
; Remove every element that is :fiat
(remove #(= :fiat %) cars)
=> (:bmw "mercedes" :honda)

; After all of this, our vector remains the same!
(println cars)
[:fiat :bmw mercedes :honda]
=> nil

What was this!? (remove #(= :fiat %) cars)

Here is a cool example of Clojure's concise powers... remove is a function that takes a function that returns something truthy or falsy, if truthy, nuke it from the list if false, it stays.
BUT #(= :fiat %) ISNT A FUNCTION!?
If you need a function quickly you can create one using the short syntax where you prefix an open bracket with '#' and you reference arguments using % (and %1 %2 and so if there are more than one).
You can also create a quick function that has no name using:

  (fn [arguments here]
      (println "arguments=" arguments here))

Binding Using Let

Let is the last thing I want to go over in this quick intro to Clojure.

Let is a function that binds the results of other functions to symbols which you can use.
Think of these symbols like variables you would create in the body of functions that you would use in other languages.

Here is an example of a function using let:

(defn add-counts
  [a b]
  (let [count-a (count a)
        count-b (count b)]
    (+ count-a count-b)))

(add-counts "dan" "clojure")
=> 10

What we do, is let count-a hold the value of the result from (count a)
and let count-b hold the value of the result from (count b).
We then use those values and return the result.
Once the function is finished, the values are lost!

  ; Define a list of cars
  (def cars [:fiat :bmw "mercedez" :honda])

  (defn remove-fiats
    (let [is-fiat? #(= :fiat %)] ;; bind the shorthand function to is-fiat?
      (remove is-fiat? my-vector)))

  (remove-fiats cars)

  => (:bmw "mercedez" :honda)

Here we bind a shorthand function to is-fiat? and pass that function to remove, instead of creating it in the remove function.
(remove #(= :fiat %) cars) is equivalent to
(remove is-fiat? my-vector) in that context.

And that's pretty much it for let :)

Example Project

I've created a really simple project, which I made to showcase many more built-in Clojure functions.
You can find it here:

This is my first blog post like this, but I'm planning on doing more. If you'd like more of this kind of thing please subscribe, or get in-touch with me on LinkedIn.