Your first Clojure code

In this post I want to give a very quick tour of Clojure. This is aimed at people that might not have done any Lisp, Java or Functional programming before. Of course it's not going to be complete but should be enough to get you to write a few bits of code and perhaps solve some puzzles.


If you're on Mac and use homebrew, just do brew install clojure. Otherwise check out the official instructions.

Check that your installation works fine by typing clj — you should see something like:

Clojure 1.9.0  

Hit Ctrl-C to exit this.

Note: you will notice that it takes a few second to get to this prompt. You will suffer through this during this introduction, but when doing proper Clojure development, the startup time becomes a non-issue for most workflows.

Hello world!

Create a file named hello.clj, with these contents:

(println "Hello, world!")

You can run it with clojure hello.clj, and you should see the results printed on your terminal.

So what's going on? println is a function. "Hello, world!" is a String. You invoke a function by enclosing it and its arguments with parentheses. In other languages, you'd do println("Hello world!") which is a bit similar.

Clojure being a dynamic language, you can pass any kind of argument to println — try adding those to hello.clj and invoke it again with clojure hello.clj.

(println 123) ;; this is a comment

(println true) ;; true/false are booleans

(println nil) ;; nil is null

(println [1 2 3]) ;; vector definition, commas are optional

You should see this:

Hello, world!  
[1 2 3]

So a .clj file can contain one or more top-level expressions. They will be evaluated one by one, top-to-bottom, as in most programming languages. Clojure also has numbers, booleans, nil and a few pretty cool container data structures.


Clojure also an interactive prompt; you saw it when you run clojure without a command. You can try all this code in the REPL, like so:

$ clj
Clojure 1.9.0  
user=> (println "hi again")  
hi again  

This time, you see the printed line, but you also see nil — this is because the REPL will print out the return value of each expression as you type it in. In this case, the println function returns nil.

There is an alternative form of println called println-str — instead of printing directly, it will instead return a string. Let's try it out:

user=> (println-str "another try")  
"another try\n"

This time, nothing is printed, and we just get a string back. You can see the newline at the end.

Note: moving on, I'll showcase REPL-based code, but you can run all those just by adding them to your hello.clj file, and running clojure hello.clj. Note how we use clojure to run programs but clj to get the REPL.


Let's try to capture the value returned by println-str and print it later:

user=> (def a "hello")  
user=> a  
user=> (println a)  

Notice how to define a variable, we just call a function — this time the function is def and takes two arguments: the variable name which is a Symbol, and the variable value, which can be anything. Notice how def also returns this weird-looking #'user/a thingy — this is the internal representation of "global" variables in Clojure. However, when you evaluate the Symbol, that is, a, you get the value back, which is "hello". You can use the name of the variable as you would expect when calling functions etc.

Of course, the value of the variable can be pretty any expression — in this case, the expression will be evaluated, and its return value will be used:

user=> (def my-var (println-str "variables are cool"))  
user=> my-var  
"variables are cool\n"
user=> (println my-var)  
variables are cool


Notice how we now get two newlines when using println — the one that my-var contains, and another one that println adds.

Also notice how my-var can contain a dash. Clojure is very permissive on what can be used in identifiers (Symbols, technically). You might have guessed that already, since println-str contains a dash.

Note: println-str is a very little used function, but makes sense for this tutorial. We'll cover more useful functions soon!


Let's define our own function — it will just call the only two functions we have used so far:

user=> (defn say-it*3 [x]  
          (println x)
          (println x)
          (println x))
user=> (say-it*3 "hi!")  

Let's break this apart — to define a function, just call another function, in this case defn.

Now, defn is technically a macro, but it looks and behaves just like a function for our purposes. Its arguments are:

  • a Symbol — the name of the function, say-it*3 (wow! you can have a * in your function name!)
  • a vector of arguments, that is, [x], meaning "this functions takes a single argument named x
  • a bunch of expressions, that when the function is called, will be evaluated top-to-bottom. The return value of the last expression will be returned (there is no return or similar keyword).

As you can see, defn returns the same kind of var as def returns.

You call this function as if it was any other function, by surrounding it and its arguments with parentheses. Of course, functions can have more than one arguments:

user=> (defn another! [a b c]  
  (println a)
  (println b)
  (println c))
user=> (another! 1 2 3)  

...or even no arguments:

user=> (defn -*- []  
  (println "konnichi-wa!"))
user=> (-*-)  

And yes, you can have a lot of weirdly named functions — please don't do it :)

The fact that you can use a lot of characters for symbols comes directly from Clojure's minimal syntax — there are very few special operators and rules, so even arithmetic stuff is done by just calling functions:

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

In this case +, * and = are just plain functions, and you call them with as many arguments as you want.

You can also have anonymous functions, like so:

user=> (fn [x] (println x))  
#object[user$eval183$fn__184 0x4bff64c2 "user$eval183$fn__184@4bff64c2"]

Note: Ignore the weird return value — this is a Clojure-internal representation.

See how fn is really similar to defn — you just don't give it a name. Let's combine fn and def to actually capture the function in a variable:

user=> (def anonymous (fn [x] (println x)))  
user=> anonymous  
#object[user$anonymous 0x4566d049 "user$anonymous@4566d049"]
user=> (anonymous "hi")  


The fundamental way to do conditionals is by calling the if function:

user=> (if (= 1 2) "wat?" "ok!")  

We call if we 3 arguments:

  • The condition, in our case, the result of (= 1 2), i.e. false
  • The "true" expression, i.e. "wat?"
  • The "false" expression, i.e. "ok!"

The return value is either the "true" or "false" expression. Here's a more complicated example:

user=> (if (= 1 "1") (println "wat?") (println "ok!"))  

Notice how although only one expression is even evaluated — this is a special property of if that is provided by the Clojure runtime. If if was a normal function, we'd see both wat? and ok! printed.

Notice also that the return value is nil, since this is what println returns.

Another useful special function is do:

user=> (do  
         (println 1)
         (println 2)
         (println 3)

This looks very similar to defn and fn — it evaluates expressions one-by-one, and returns the value of the last one (in our case, just the string "done!"). By combining if and do you can get more useful constructs:

user=> (if (= 1 1.0)  
 (println "Equality is weird")
  (println "Equality is sane")
Equality is sane  

By the way, if you are dealing only with numbers, you can use the == function instead of =:

user=> (== 1 1.0)  

Note: There are more conditionals available, but we'll stick with if for a while


Many programming languages have intricate rules about scoping — that is, from where you can access and change a variable. Clojure, on the other hand, is simple:

  • Variables defined with def are always global
  • Variables defined with let are always local

Note: this is a simplification, but it can get you very far.

In reality, you always use def sparingly, and always at the "root" level of a file. You use let everywhere else. How does let look like? It's just, as you may have guessed, a function:

user=> (let [local 1]  
          (println "This is my scope")
          (println "The value is" local))
This is my scope  
The value is 1  

Let looks a bit similar to defn and fn in that its first argument is a vector, that is [local 1]. The rest of the arguments are just expressions that are evaluated one-by-one, like in do. The return value is, again, the value of the last expression, in our case nil.

So how does this work? In other languages, you'd do local = 1, then in the same "scope" you'd be able to use local to refer to the value 1. This is similar, the scope being the body of let. After the closing ) of let, local is not valid anymore.

A more involved example:

user=> (let [a 1  
             b 2
             c 3]
            (* a b c))

Here we assign multiple variables, a, b, and c at the same time. We just multiply them together, and we get the result back. Another one:

user=> (let [a 2  
             b 3
             aa (* a a)
             bb (* b b)
             aabb (+ aa bb)]
             (= aabb 13))

In this case, not only we assign multiple variables, we also use previously-assigned variables. The variable assignment is done top-to-bottom, so we just have to make sure everything is in the right order.

Functions everywhere

You might have noticed that to do very common stuff like defining a variable, a function, comparing or even adding two numbers together, we just call a function. This is a common attribute for any Lisp-like language, and there's no other syntax to them. In real-life usage though, there are a handful of functions that are so commonly used that they effectively become the syntax.

The everything-is-a-function is a bit of a simplification. Some things are macros, other things are special forms. But when you use them, usually you don't think about it, and you call them as if they were functions.

To be continued! Rather than let this sit in my drafts, I'm publishing it now in order to get some feedback. I will either add to this or post something new later.

Please post your feedback on this Reddit thread