# Day 4 – Functions

In Clojure functions are first-class objects, that is, it does all the basic operations with the object, such as passing it to a function, returning from a function, and binding it to a variable.

It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures. — Alan Perlis

# Calling functions

All Clojure operations have the same syntax: opening parentheses, operator, operands, closing parentheses.

(operator operand1 operand2 operandn)

You may not have noticed, but so far you have seen many function calls.

(+ 2 3)
;; user=> 7

(first [3 5 8])
;; user=> 3

((or + -) 2 3 4)
;; user> 9

Generally, functions can use expressions as arguments, including other functions.

(inc 1.1)
;; user=> 2.1

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

The last thing you need to know about function calls is that Clojure evaluates all function arguments recursively before passing them to the function.

(+ (inc 70) (* 7 (- 13 1)))
;; user=> 155

;; (+ (inc 70) (* 7 (12)))
;; (+ (inc 70) 84)
;; (+ 71 84)
;; 155

# Defining Functions

In Clojure, role definitions consist of five main parts:

  • defn
  • Function name
  • A docstring describing the function (optional)
  • Parameters listed in brackets
  • Function body
(defn hello-name
  "Return greetings to someone"
    [name]
      (str "Hello, " name "!"))

(hello-name "John")
;; user=> "Hello, John!"

(hello-name "Rich")
;; user=> "Hello, Rich!"

(hello-name "Mark")
;; user=> "Hello, Mark!"

# The docstring

The docstring is a useful way to describe and document your code. You can view the docstring for a function in the REPL with (doc fn-name) — for example, (doc map).

(doc hello-name)
;; user/hello-name
;; ([name])
;;   Return greetings to someone
;; nil

# Parameters and Arity

Clojure functions can be defined with zero or more parameters and the number of parameters is the function’s arity.

;; 0-arity function
(defn no-params
  []
  "I take no parameters!")

;; 1-arity function
(defn one-param
  [p]
  (str "I take one parameter: " p))

;; 2-arity function
(defn two-params
  [p1 p2]
  (str "Two parameters! " p1 " and " p2))

;; multi-arity function
(defn multi-arity
  ;; 3-arity arguments and body
  ([first-arg second-arg third-arg]
     (do-things first-arg second-arg third-arg))
  ;; 2-arity arguments and body
  ([first-arg second-arg]
     (do-things first-arg second-arg))
  ;; 1-arity arguments and body
  ([first-arg]
     (do-things first-arg)))

Clojure also lets you define arity-variable functions by including a rest parameter, which puts the rest of these arguments in a list. The rest parameter is indicated by an ampersand &.

(defn hello
  [guy]
    (str "Hello, " guy "!"))

(defn bot-codger
  [& guys]
    (map hello guys))

(bot-codger "John" "Nathan" "Tony")
;; user=> ("Hello, John!" "Hello, Nathan!" "Hello, Tony!")

# Destructuring

The destructuring lets you link names to values within a collection.

(defn first-element
  [[first-thing]]
  first-thing)

(first-element [:el1 :el2 :el3])
;; user=> :el1

Clojure associate the name lat with the value corresponding to the key :lat (same for lng).

(defn location
  [{lat :lat lng :lng}]
    (println (str "lat: " lat))
    (println (str "lng: " lng)))

(location {:lat 28.22 :lng 81.33})
;; user=> lat: 28.22
;; user=> lng: 81.33

# Function body

The function body can contain any type and will always automatically return the last evaluated value.

(defn another-function
  []
  (+ 1 13)
  21
  "function")

(another-function)
;; user=> "function"

(defn greater-than-10
  [n]
  (if (> n 10)
    (str n " is greater than 10")
    (str n " is not greater than 10")))
(greater-than-10 5)
;; =>user "5 is not greater than 10"

(greater-than-10 11)
;; =>user "11 is greater than 10"

# Anonymous Functions

In Clojure, functions don’t need to have names.

(fn [param]
  :body)

You could even associate your anonymous function with a name.

(def my-special-multiplier (fn [x] (* x 3)))
(my-special-multiplier 12)
;; user=> 36

Clojure also offers another, more compact way to create anonymous functions by just adding the preceding #.

#(* % 3)

(#(* % 3) 8)
;; user=> 24

(map #(str "Hi, " %)
     ["Annonymous" "Function"])
;; user=> ("Hi, Annonymous" "Hi, Function")

%, indicates the argument passed to the function (%1, %2, %3, and so on).

# Return Functions

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

(defn inc-maker
  "Create a custom incrementor"
  [inc-by]
  #(+ % inc-by))

(def inc3 (inc-maker 3))

(inc3 7)
;; user=> 10

inc-by is in scope, so the returned function has access to it even when the returned function is used outside inc-maker.

(defn inner
  [from-outer]
    (fn [] (println from-outer)))

(def fn1 (inner "this is from fn1"))

(def fn2 (inner "this is yet another from fn2"))

(fn1)
;; user=> this is from fn1
;; user=> nil

(fn2)
;; user=> this is from fn2
;; user=> nil

# Test your knowledge

  • Define a function always-thing which takes any number of arguments, ignores all of them, and returns the number 100.

  • Create a function that calculates the cube of a given number.

  • Define a function one-less-arg that takes two arguments:

    • f, a function
    • x, a value
  • and returns another function which calls f on x plus any additional arguments.

# Day 5 - Namespaces

See more


# License

This project is licensed under the MIT License.

License (opens new window)