This guide is not meant to make any arguments about why to learn Clojure but is only meant to give a very quick introduction to syntax basics. I think Clojure is a very elegant language, but the "weird" syntax often puts people off. I will attempt to show so you some basics so you can get an idea of what Clojure looks and feels like.
⚠️ Warning: This guide leaves out a bunch of details and makes some oversimplifications for the sake of brevity. It also assumes that you are comfortable with basic programming concepts.⚠️
Trying It Out
If you want to code along and run any of the following code on your computer then the easiest way to do that is with the REPL.
Just install Clojure (see the official guide) and type clojure
in your terminal.
This will start an interactive environment in which you can run Clojure code.
You can verify everything is working by running (println "Hello, World!")
Invoking Functions
If you ran the above snippet, you already invoked your first function, but it is worth looking at the anatomy of a Clojure function invocation.
(function-name arg-1 arg-2 ... arg-n)
The parentheses instruct Clojure to invoke a function where the value in the first position is the function name, and the remaining values are arguments to that function.
(+ 1 2) ;=> 3
In the above example, +
is the function and 1
and 2
are the arguments, resulting in the value 3
.
If you try this in the REPL then the result will be printed to the terminal.
Defining Variables
You can use def
if you need to store a value in an easier-to-remember name.
To keep things simple, you can think of these being defined globally.
(def x 10)
(def y 15)
(+ x y) ;=> 25
Defining Functions
To define functions, you use defn
. The generic structure of a function definition looks like this.
(defn function-name
[arg-1 arg-2 ... arg-n]
body-expression-1
body-expression-2
...
body-expression-n)
For example, following function takes a name and prints a personalised greeting.
(defn greet
[name]
(println "Hello there, " name))
;Call the function you just created
(greet "Alice")
;"Hello there, Alice
;=> nil
Two lines are printed because the greeting is printed to the console with println
and so is the return value of the function.
Every function returns a value and in this case, the function returns nil
(Clojure's version of a null
value).
The value of the last expression within a defn
is the return value. For example, you could return the name of the greeted person.
(defn greet
[name]
(println "Hello there, " name)
name)
(greet "Alice")
;Hello there, Alice
;=> "Alice"
Define Scoped Variables
To define locally scoped variables, you can use let
.
;a will only be available within the parenthesis of the let
(let [a 1]
(println a)) ;=> 1
;a is not available anymore
(println a) ;=> Unable to resolve symbol: a in this context
You can define multiple variables within a single let too.
(let [a 1
b 2]
...)
You can, for example, extract the greeting from our above example into a variable to print and return it.
(defn greet
[name]
(let [greeting (str "Hello there, " name)]
(println greeting)
greeting))
Sequencial Collections
The most straightforward list collection to use are vectors.
You can create one like this.
(def names ["Alice" "Andrew"])
Values within a vector are separated by spaces and you can mix types, but you probably should not.
You can print greetings for a list of names.
(defn greet
[name]
(println "Hello there," name))
(def names ["Alice" "Andrew" "R2-D2"])
(for [name names]
(greet name))
;Hello there, Alice
;Hello there, Andrew
;Hello there, R2-D2
Or only greet the first name in the list
(greet (first names))
; Hello there, Alice
Maps
The second core collection is a map that is useful for storing key-value pairs. You can use anything you like as a key or value.
A simple person map could look like this
(def person
{"name" "Alice"
"age" 20})
And a value can be accessed with the get
function.
(get person "name") ;=>Alice
This works just fine, but often there is a better type you can use for keys and those are so called keywords
. A keyword is, more or less, an identifier with special attributes and written with a leading colon, e.g. :name
.
One of the best attributes of keywords is, that they can be used as lookup functions.
(def person
{:name "Alice"
:age 20})
;This works
(get person :name) ;=>Alice
;But this is neater
(:name person) ;=Alice
Basic Control Flow
One of the simplest control flow construct in most languages is if-else
.
In Clojure the generalised version looks like this.
(if test-expression
then-expression
else-expression)
Let's say you want to modify our greeting for anyone over the age of 50.
(defn build-greeting
[person]
(if (> (:age person) 50)
(str "Good Evening, " (:name person))
(str "Hey there, " (:name person))))
(build-greeting {:name "Andrew" :age 51})
=> "Good Evening, Andrew"
(build-greeting {:name "Alice" :age 20})
=> "Hey there, Alice"
Putting it together
Let's write a bit of code that prints greetings for a list of people.
(def people [{:name "Alice"
:age 20}
{:name "Andrew"
:age 55}])
(defn build-greeting
[person]
(if (> (:age person) 50)
(str "Good Evening, " (:name person))
(str "Hey there, " (:name person))))
(defn greet-all
[people]
(for [person people]
(let [greeting (build-greeting person)]
(println greeting))))
(greet-all people)
;Hey there, Alice
;Good Evening, Andrew
Congrats! You now know the basics of Clojure.
If you want to learn more then I can recommend the following resources:
- Official Getting Started Guide
- Clojure for the Brave and True (Free Online Book)
- Programming Clojure (Book)
Top comments (3)
Great article! Thank you!
Very interesting article :)
Thank you !
🙌🙌 Great introduction to Clojure. I needed this after heading in way too deep these past few days 😂.