Functions you write
defn, a name that can be called in more than one way, arguments taken apart as they arrive, and the value a function hands back.
A name, some arguments, an answer
(defn greet [who] (str "Hello, " who "!")) builds a function and gives it a name. The vector after the name lists the arguments, and the body is everything that follows. Calling (greet "Ada") gives "Hello, Ada!". The vector is not a list of types: it is a list of names, and the values in the call are lined up with them in order.
The last expression in the body is the answer. There is no return to write, because the last thing worked out is what the call is worth. A function that only prints, such as println, hands back nil — worth knowing when a function is supposed to print something and answer something too. A body with three expressions is allowed; the first two run for their effects and the third is the answer.
Several ways to call the same name
A function can offer more than one set of arguments: each set goes in its own bracketed pair. (defn greet ([] (greet "world")) ([who] ...)) makes (greet) legal, and the shorter version is free to call the longer one. That is how a default gets built without a special kind of argument. Each arity is a separate clause of the same name, and the clause with fewer arguments can call the fuller one rather than repeating the work.
Which version runs is decided by how many arguments the call has, so keep the sets clearly different and a reader never has to guess.
Taking an argument apart where it arrives
A function can also name the pieces of a collection it was given: (defn from-pair [[x y]] ...) takes a two-item value and calls the pieces x and y. The shape it expects sits exactly where the arguments are listed, which is where a reader is already looking. The pattern is a match, not a set of instructions: it says the argument must be a two-item row, and if it is not, the call fails at the boundary rather than deep in the body.
The same trick works on maps, and it is the reason a function that takes a record can read like a sentence. The next chapter shows it with let, where a name is made inside a function instead of in the argument list.
Arguments are worked out before the body runs
When you write (postage (+ 1 2)), Clojure works out (+ 1 2) first and hands the body the value 3. The body never sees the expression, only the result, which is why an argument can be any expression of the right shape. This is ordinary evaluation, not a special rule for functions: the same thing happens inside (+ 1 (* 2 3)), where the inner call finishes before the addition starts.
The same is true when a function is used as an argument to another function: the inner call finishes before the outer one starts. Reading a call from the inside out is the whole of the evaluation order, and it explains why a long line can be split into names without changing what it does.
A function is a value with a name
defn is shorthand for making a function and giving it a name. The function itself is a value, so it can be stored, passed to another function, or handed back; the name is just the easiest way to keep hold of it. A function passed to map is the same value as the one you call by name, so (map greet people) and (map #(greet %) people) do the same work.
Defining one does not run it. The body waits until a call supplies the arguments, which is why a file can define several functions before any output appears, and why the printed output follows the calls at the bottom of the file.
Two ways to be called, and an argument taken apart
(defn greet
([] (greet "world"))
([who] (str "Hello, " who "!")))
(defn add-up [xs]
(reduce + xs))
(defn from-pair [[x y]]
(str "x is " x ", y is " y))
(println (greet))
(println (greet "Ada"))
(println (add-up [1 2 3 4]))
(println (from-pair [3 4]))A name, some arguments, an answer
Run it and read the five lines. Then write a `tax` function that takes a price and a rate and answers the tax, and print the total for a price of 40 at a rate of 5.
Keep a function small enough to read in one glance and to try on its own. When it grows a second job, give that job its own name: a well-chosen function name is documentation that cannot drift away from the code.
Three mistakes are easy to make. A function whose body ends in println answers nil, so wrapping it in str or arithmetic will not use the printed text. Calling a multi-arity function with a count no branch accepts stops with an arity error, and the message lists the counts that would have worked. And defining a function with a core name such as count or map shadows the core function for every line below it, so the error appears where you call the original, not where you redefined it.
Try these next
- Write a one-argument function that squares a number, and call it twice.
- Add a no-argument version that calls the one-argument version with a default.
- Write a function that takes a two-item vector apart and prints both pieces.
Postage, and a label taken apart
Run it and read the five lines, including the nil at the end. Then change the base rate from 2 to 3, and add a three-argument version that adds 4 for tracking when its third argument is true.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
What does a function hand back to its caller?
