There are many ways to define a function and parse arguments. In this chapter, we'll learn more about function definition, destructuring and some helpful higher-order functions.
So far we have seen the usage of
defn to define functions. But most of our examples were contrived and not close to real-world use cases.
At work, the functions we define might need to handle a variable number of arguments, or they might need to destructure a large map passed in as an argument.
In this chapter, we'll learn various aspects of defining functions in Clojure. The examples in this chapter are coded in the
Q. What is the disk path of the
Multi arity functions#
Multi arity functions accept different numbers or types of variables. They work by defining different execution paths for different arguments and are generally used to provide default arguments:
Multi arity functions are conceptually similar to function overloading.
Anonymous (or inline) functions are functions that have no home - ie are not namespace-qualified. They exist only at the point of definition. They are useful for a variety of purposes, like defining callbacks or simple predicates.
There are two ways to define anonymous functions:
defn have similar signatures, except the function name is not present while using
fn. With the shorthand, we completely skip the arguments vector and access the arguments using the
%n sign, where
n is the index of the argument, starting at 1.
Anonymous functions are just like normal functions and can be evaluated and called directly.
If you need to capture only one argument, you can skip the index and simply use
%. Anonymous functions are helpful when working with sequence operations like
reduce. We'll see them in action soon.
Functions defined with
fn can follow an alternate signature to specify inline documentation:
Variable arity functions#
Variable arity or variadic functions lets you capture any number of arguments as a list. We have seen this function already, in the
Variadic functions use
& ampersand to signify a variable number of arguments:
rest variable is available in the function scope as a list.
If we call
f with five arguments:
(f 1 2 3 4 5), then
arg1's value will be
rest will be a list of four elements:
'(2 3 4 5).
We have so far called functions by passing in the arguments manually. For example,
f defined above is called by typing
(f 1 2 3 4 5). But what if you wanted to call
f for a list of n integers? What if these arguments were stored in a variable?
apply function lets you "unpack" sequences and use them as function arguments.
This is the same as calling:
Except, the arguments are now a vector (or some other variable).
In UI programming frameworks like React, it's common to pass down
props (or maps) to child components. It's also fairly common to pass configuration maps as function arguments.
Sometimes, you might need only a small part of the argument. Destructuring helps you pull out the required pieces precisely.
Vectors and lists#
Let's suppose a function that takes a three-element vector as an argument. This vector is fetched directly from the database and holds the name, age and height in cms of a user, in that order. If we wanted to get the name, age and height with what we know so far, we could write something like:
This doesn't spark joy. With sequence destructuring, the same code can be rewritten as: