Skip to content

Clojure Syntaxλ︎

The Clojure syntax is very small and is actually a data structure, defined as a list, (), with the first element of a list being a function call and all other elements arguments to that function.

Examples are editable (using an embedded REPL) so feel free to experiment and watch as the return value changes as you change the code. Reload the page if you want to reset all the code back to the starting point.

edn based notationλ︎

The core Clojure syntax is defined in the extensible data notation (edn). edn demonstrates that Clojure code is defined as a series of data structures

Clojure adds an execution model on top of edn to make a programming language and is a super-set of edn.

edn is used as a data transfer format, especially for Datomic the Clojure transactional database

Calling functionsλ︎

The first element in a list, (), is treated as a call to a function. The examples show how to call functions with multiple arguments.

(+ 1 2)
(+ 3 (* 2 (- 7 2) 4) (/ 16 4))
(str "Clojure is " (- 2020 2007) " years old")
(inc 1)
(map inc [1 2 3 4 5])
(filter odd? (range 11))

Hint::Prefix notation and parensλ︎

Hugging code with () is a simple syntax to define the scope of code expressions. No additional ;, , or spaces are required.

Treating the first element of a list as a function call is referred to as prefix notation, which greatly simplifies Clojure syntax. Prefix notation makes mathematical expressions completely deterministic, eliminating the need for operator precedence.

Understanding functionsλ︎

Functions contain doc-strings describing what that function does. The doc function returns the doc-string of a particular function. Most editors also support viewing of doc-strings as well as jumping to function definitions to view the source code

(doc doc)

Strongly typed under the coversλ︎

Clojure is a dynamically typed language so types do not need to be explicitly defined, although type hints can be added for performance where required.

Clojure is strongly typed and everything is a type underneath, relative to the host platform (Clojure uses Java types, ClojureScript uses JavaScript types). The type of anything in Clojure can be returned using the type function.

(type 42)
;; (type {:hash "data" :map "more data"})
(type {:hash "data" :map "more data"})

Modeling data with Collection typesλ︎

Clojure has 4 main collection types, all immutable (cannot change once created) and can contain any Clojure types.

(str "lists used mainly " (* 2 2) " " :code)
[0 "indexed" :array (* 2 2) "random-access"]
{:key "value" "hash-map" "also referred to as dictionary"}
#{1 2 3 4 "unique" "set" "of" "values" "unordered" (* 3 9)}

Hint::Persistent data typesλ︎

To change data in Clojure new copies are created rather than changing existing values. The copies of data will share values from the original data that are common in both. This sharing is called persistent data types and enables immutable data to be used efficiently.

Defining names for values (vars)λ︎

Names can be bound to any values, from simple values like numbers, collections or even function calls. Using def is convenient way to create names for values that are shared in your code.

evaluating a name will return the value it is bound to.

(def public-health-data
  ({:date "2020-01-01" :confirmed-cases 23014 :recovery-percent 15}
   {:date "2020-01-02" :confirmed-cases 23014 :recovery-percent 15}
   {:date "2020-01-03" :confirmed-cases 23014 :recovery-percent 15}))

public-health-data

Hint::def for shared values, let for locally scoped valuesλ︎

let function is used to bind names to values locally, such as within a function definition. Names bound with def have namespace scope so can be used with any code in that namespace.

Using data structuresλ︎

Using the map and inc function, increment all the numbers in a vector

(map inc [1 2 3 4 5])

The above map function is roughly equivalent to the following expression

(conj [] (inc 1) (inc 2) (inc 3) (inc 4) (inc 5))

The conj function creates a new collection by combining a collection and one or more values.

map reduce filter are common functions for iterating through a collection / sequence of values

(map * [1 3 5 8 13 21] [3 5 8 13 21 34])
(filter even? [1 3 5 8 13 21 34])
(reduce + [31 28 30 31 30 31])
(empty? [])

Defining custom functionsλ︎

(defn square-of
  "Calculates the square of a given number"
  [number]
  (* number number))

(square-of 9)

Function definitions can also be used within other expressions, useful for mapping custom functions over a collection

(map (fn [x] (* x x)) [1 2 3 4 5])

Host Interoperabilityλ︎

The REPL in this web page is running inside a JavaScript engine, so JavaScript functions can be used from within ClojureScript code (ClojureScript is Clojure that runs in JavaScript environments).

In the box below, replace () with (js/alert "I am a pop-up alert")

()

JavaScript libraries can be used with ClojureScript, such as React.js

(defn concentric-circles []
  [:svg {:style {:border "1px solid"
                 :background "white"
                 :width "150px"
                 :height "150px"}}
   [:circle {:r 50, :cx 75, :cy 75, :fill "green"}]
   [:circle {:r 25, :cx 75, :cy 75, :fill "blue"}]
   [:path {:stroke-width 12
           :stroke "white"
           :fill "none"
           :d "M 30,40 C 100,40 50,110 120,110"}]
   [:path {:stroke-width 12
           :stroke "white"
           :fill "none"
           :d "M 75,75 C 50,90 50,110 35,110"}]])