DEV Community

Cover image for A Very Short Introduction to Clojure
Jakob Durstberger
Jakob Durstberger

Posted on

A Very Short Introduction to Clojure

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!")

Image description

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

You can define multiple variables within a single let too.

(let [a 1
      b 2]
   ...)
Enter fullscreen mode Exit fullscreen mode

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))
Enter fullscreen mode Exit fullscreen mode

Sequencial Collections

The most straightforward list collection to use are vectors.
You can create one like this.

(def names ["Alice" "Andrew"])
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Or only greet the first name in the list


(greet (first names))
; Hello there, Alice
Enter fullscreen mode Exit fullscreen mode

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})
Enter fullscreen mode Exit fullscreen mode

And a value can be accessed with the get function.

(get person "name") ;=>Alice
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Congrats! You now know the basics of Clojure.

If you want to learn more then I can recommend the following resources:

Top comments (3)

Collapse
 
geazi_anc profile image
Geazi Anc

Great article! Thank you!

Collapse
 
fredericalix profile image
Frederic Alix

Very interesting article :)
Thank you !

Collapse
 
nicktolhurst profile image
Nick Tolhurst

🙌🙌 Great introduction to Clojure. I needed this after heading in way too deep these past few days 😂.