Clojure - Functions

2 minute read

Functions

Functions are a primary building block in Clojure. They can be used to craft complex programs from small and simple blocks.

Calling functions

1(+ 1 2 3 4)
2(* 1 2 3 4)
3(first [1 2 3 4])

In FP languages we treat functions as first class citizens meaning we can use them like any other datatype. This allows us to pass them to other functions making higher order functions - functions that take functions as parameters. An example of such function would be map which creates a new list by applying a function to each member of a collection.

1(map inc [0 1 2 3])
2; => [1 2 3 4]

Defining functions

Function definitions are composed of five parts:

  • defn
  • function name
  • an optional docstring
  • parameters listed in brackets
  • function body

The Docstring

Docstring represents a useful way to describe and document code. Dostrings can be viewed in REPL with (doc fn-name).

Parameters and Arity

Clojure functions support arity overloading meaning one function can be defined to do different things depending on the number of arguments passed to it.

1(defn multi-arity
2  ([first-arg second-arg]
3    (do-thing first-arg second-arg))
4  ([first-arg]
5    (do-thing first-arg)))

Clojure allows defining of variable-arity functions by including a rest parameter(&)

 1(defn greet
 2  [name]
 3  (str "Hello, " name " !"))
 4
 5(defn greet-many
 6  [& people]
 7  (map greet people))
 8
 9(greet-many "Bill" "Anne")
10; => ("Hello, Bill !" "Hello, Anne !")

Destructuring

Destructuring is used to concisely bind names to values within a collection.

1(defn first-thing
2  [[f]]
3  f)
4
5(first-thing [1 2 3 4])
6; => 1

Maps can also be destructured in a similar way.

 1(defn greet-full
 2  [{name :name surname :surname}]
 3  (str "Hello, " name " " surname " !"))
 4
 5(greet-full {:name "Bill" :surname "Clinton"})
 6; => "Hello Bill Clinton !"
 7
 8; Shorter syntax
 9(defn greet-full
10  [{:keys [name surname]}]
11  (str "Hello, " name " " surname " !"))

Anonymous Functions

Functions that don’t have names are called anonymous functions. In Clojure we can define them in two ways:

1(fn [x] (* x 3))
1#(* % 3)

The percent sign indicated the argument passed to the function. When a anonymous function takes multiple parameters we can distinguish them like this: %1, %2, %3 …

Returining Functions

Functions can return other functions and the returned functions are closures meaning they can access all the variables that were in scope when the function was created.

1(defn inc-maker
2  [inc-by]
3  #(+ % inc_by))
4
5(def inc3 (inc-maker 3))
6
7(inc3 7)
8; => 10