Function definitions
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 first-project.fn-defs
namespace.
Q. What is the disk path of the first-project.fn-defs
file?
A.
touch src/first_project/fn-defs.cljs
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:
(defn hello-world
([] (hello-world "there"))
([name] (str "Hello " name)))
(hello-world) ;; => "Hello there"
(hello-world "Newline") ;; => "Hello Newline"
Multi arity functions are conceptually similar to function overloading.
Anonymous functions#
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:
with
fn
functionwith
#()
shorthand
;; anonymous function
(fn [a b] (+ a b 5))
(let [add5 #(+ %1 %2 5)]
(add5 1 1)) ;; => 7
fn
and 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 map
and reduce
. We'll see them in action soon.
Docstrings#
Functions defined with defn
or fn
can follow an alternate signature to specify inline documentation:
(defn fn-name
"A line about what this function does"
[arg1 arg2 arg3]
;; body
)
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 first-project.core
namespace.
Variadic functions use &
ampersand to signify a variable number of arguments:
(defn f [arg1 & rest]
(prn arg1 rest))
The 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 1
and rest
will be a list of four elements: '(2 3 4 5)
.
apply
function#
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?
The apply
function lets you "unpack" sequences and use them as function arguments.
(apply f [1 4 5 2 4 5 2 42])
This is the same as calling:
(f 1 4 5 2 4 5 2 42)
Except, the arguments are now a vector (or some other variable).
Destructuring arguments#
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:
(defn process-db-record [p]
;; p is a three element vector
(let [name (first p)
age (second p)
height (last p)]
;; do something to name age and height
))
This doesn't spark joy. With sequence destructuring, the same code can be rewritten as:
(defn process-db-record2 [[name age height]]
;; do something to name age and height
)
This page is a preview of Tinycanva: Clojure for React Developers