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 user=>
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.
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
(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! 123 true nil [1 2 3]
.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 nil user=>
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-str — instead of printing directly, it will instead return a string. Let's try it out:
user=> (println-str "another try") "another try\n" user=>
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=> a "hello" user=> (println a) hello nil user=>
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 user=> my-var "variables are cool\n" user=> (println my-var) variables are cool nil user=>
Notice how we now get two newlines when using
println — the one that
my-var contains, and another one that
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.
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 user=> (say-it*3 "hi!") hi! hi! hi! nil user=>
Let's break this apart — to define a function, just call another function, in this case
defn is technically a macro, but it looks and behaves just like a function for our purposes. Its arguments are:
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
- 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
returnor similar keyword).
As you can see,
defn returns the same kind of
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! user=> (another! 1 2 3) 1 2 3 nil user=>
...or even no arguments:
user=> (defn -*-  (println "konnichi-wa!")) #'user/-*- user=> (-*-) konnichi-wa! nil 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) 3 user=> (* 3 4) 12 user=> (+ 1 2 3 4 5) 15 user=> (= 1 2) false user=> (= 2 (+ 1 1)) true user=> (= 2 2 2) true
In this case
= 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"] user=>
Note: Ignore the weird return value — this is a Clojure-internal representation.
fn is really similar to
defn — you just don't give it a name. Let's combine
def to actually capture the function in a variable:
user=> (def anonymous (fn [x] (println x))) #'user/anonymous user=> anonymous #object[user$anonymous 0x4566d049 "user$anonymous@4566d049"] user=> (anonymous "hi") hi nil user=>
The fundamental way to do conditionals is by calling the
user=> (if (= 1 2) "wat?" "ok!") "ok!" user=>
if we 3 arguments:
- The condition, in our case, the result of
(= 1 2), i.e.
- The "true" expression, i.e.
- The "false" expression, i.e.
The return value is either the "true" or "false" expression. Here's a more complicated example:
user=> (if (= 1 "1") (println "wat?") (println "ok!")) ok! nil user=>
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
Notice also that the return value is
nil, since this is what
Another useful special function is
user=> (do (println 1) (println 2) (println 3) "done!") 1 2 3 "done!" user=>
This looks very similar to
fn — it evaluates expressions one-by-one, and returns the value of the last one (in our case, just the string
"done!"). By combining
do you can get more useful constructs:
user=> (if (= 1 1.0) (do (println "Equality is weird") "boo!") (do (println "Equality is sane") "yay!")) Equality is sane "yay!" user=>
By the way, if you are dealing only with numbers, you can use the
== function instead of
user=> (== 1 1.0) true
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
defare always global
- Variables defined with
letare 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 nil user=>
Let looks a bit similar to
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
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)) 6 user=>
Here we assign multiple variables,
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)) true user=>
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.
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