Clojure Guides_ Language_ Functions
Clojure Guides_ Language_ Functions
This work is licensed under a Creative Commons Attribution 3.0 Unported License
(https://creativecommons.org/licenses/by/3.0/) (including images & stylesheets). The source is available
on Github (https://github.com/clojure-doc/clojure-doc.github.io).
Overview
Clojure is a functional programming language. Naturally, functions are very important part of Clojure.
(defn round
[d precision]
(let [factor (Math/pow 10 precision)]
(/ (Math/floor (* d factor)) factor)))
Functions can have doc strings (documentation strings) and it is a good idea to document functions that
are part of the public API:
(defn round
"Round down a double to the given precision (number of significant digits)"
[d precision]
(let [factor (Math/pow 10 precision)]
(/ (Math/floor (* d factor)) factor)))
The benefit of writing docstrings is that they show up in editors and the REPL:
(defn round
[^double d ^long precision]
(let [factor (Math/pow 10 precision)]
(/ (Math/floor (* d factor)) factor)))
The result of a function can also have a type hint (note that it goes in front of the argument list, not in front
of the function name):
(defn round
^double [^double d ^long precision]
(let [factor (Math/pow 10 precision)]
(/ (Math/floor (* d factor)) factor)))
Type hints sometimes allow the compiler to avoid reflective method calls when using Java interop (as the
above examples do) and may produce significantly more efficient bytecode. However, as a rule of thumb,
it is usually not necessary to use type hints. Start writing your code without them. The compiler is also free
to ignore provided hints.
The examples above use Java interop (static methods of the java.lang.Math class). As of Clojure 1.11,
clojure.math is available and provides a more idiomatic way to perform mathematical operations that
do not need type hints:
Functions can also define preconditions and postconditions that put restrictions on argument values and
the value function returns:
In the example above, we use preconditions to check that both arguments are not nil. The not-nil?
macro (or function) is not demonstrated in this example and assumed to be implemented elsewhere.
Anonymous Functions
Anonymous functions are defined using the fn special form:
(fn [x]
(* 2 x))
Anonymous functions can be bound to locals, passed between functions (higher order functions are
covered later in this document) and returned from functions:
(0 2 4 6 8 10 12 14 16 18)
(0 2 4 6 8 10 12 14 16 18)
The % in the example above means "the first argument". To refer to more than one argument, use %1 ,
%2 and so on:
Please use this reader macro sparingly; excessive use may lead to unreadable code.
"Hello, world"
This works also if you have a function stored in a local, a var or passed as an argument:
(let [f format]
(f "Hello, %s" "world"))
"Hello, world"
"Hello, world"
clojure.core/apply is usually only necessary when calling variadic functions or having the list of
arguments passed in as a collection.
Multi-arity Functions
Functions in Clojure can have multiple arities, or sets of arguments:
In the example above, the version of the function that takes only one argument (so called one-arity or 1-
arity function) calls another version (2-arity) with a default parameter. This is a common use case for
multiple arities: to have default argument values. Clojure is a hosted language and JVM (and JavaScript
VMs, for that matter) does not support default argument values, however, it does support method
overloading and Clojure takes advantage of this.
Arities in Clojure can only differ by the number of arguments, not types. This is because Clojure is a
strongly dynamically typed language and type information about parameters may or may not be available
to the compiler.
A larger example:
(defn my-range
([]
(my-range 0 Double/POSITIVE_INFINITY 1))
([end]
(my-range 0 end 1))
([start end]
(my-range start end 1))
([start end step]
(comment Omitted for clarity)))
(defn currency-of
[pair]
(let [amount (first pair)
currency (second pair)]
currency))
However, this is boilerplate code that has little to do with what the function really does. Clojure lets
developer destructure parts of arguments, for both maps and sequences.
Positional Destructuring
Destructuring over vectors (positional destructuring) works like this: you replace the argument with a
vector that has "placeholders" (symbols) in positions you want to bind. For example, if the argument is
known to be a pair and you need second argument, it would look like this:
(defn currency-of
[[amount currency]]
currency)
In the example above the first element in the pair is bound to amount and the second one is bound to
currency . So far so good. However, notice that we do not use the amount local. In that case, we can
ignore it by replacing it with an underscore:
(defn currency-of
[[_ currency]]
currency)
(defn first-first
[[[i _] _]]
i)
While this article does not cover let and locals, it is worth demonstrating that positional destructuring
works exactly the same way for let bindings:
(let [pair [10 :gbp]
[_ currency] pair]
currency)
:gbp
Map Destructuring
Destructuring over maps and records (map destructuring) works slightly differently:
(defn currency-of
[{currency :currency}]
currency)
In this case example, we want to bind the value for key :currency to currency . Keys don't have to be
keywords:
(defn currency-of
[{currency "currency"}]
currency)
(defn currency-of
[{currency 'currency}]
currency)
When destructuring multiple keys at once, it is more convenient to use a slightly different syntax:
(defn currency-of
[{:keys [currency amount]}]
currency)
The example above assumes that map keys will be keywords and we are interested in two values:
currency and :amount . The same can be done for strings:
(defn currency-of
[{:strs [currency amount]}]
currency)
and symbols:
(defn currency-of
[{:syms [currency amount]}]
currency)
In practice, keywords are very commonly used for map keys so destructuring with {:keys [...]} is very
common as well.
If you want to destructure a map that has namespaced keys, you can either specify the prefix on each
name in the binding or as a prefix on :keys itself:
;; or
(defn currency-of
[{:invoice/keys [currency amount]}] ; prefixed :keys
currency) ; bind to unprefixed symbols
Map destructuring also lets us specify default values for keys that may be missing:
(defn currency-of
[{:keys [currency amount] :or {currency :gbp}}]
currency)
This is very commonly used for implementing functions that take "extra options" (faking named arguments
support).
Just like with positional destructuring, map destructuring works exactly the same way for let bindings:
:gbp
Variadic Functions
Variadic functions are functions that take varying number of arguments (some arguments are optional).
Two examples of such function in clojure.core are clojure.core/str and clojure.core/format :
"ab"
"abc"
(format "Hello, %s" "world")
"Hello, world"
(defn log
[message & args]
(comment ...))
In the example above, one argument is required and the rest is optional. Variadic functions are invoked as
usual:
(defn log
[message & args]
(println "args: " args))
(log "message from " "192.0.0.76")
args: (192.0.0.76)
nil
nil
As you can see, optional arguments ( args ) are packed into a list.
Approaching named parameters from the standpoint of destructuring a variadic function allows for more
clearly readable function invocations. This is an example of named parameters:
(defn job-info
[& {:keys [name job income] :or {job "unemployed" income "$0.00"}}]
(if name
[name job income]
(println "No name specified")))
#'cljs.user/job-info
No name specified
nil
Without the use of a variadic argument list, you would have to call the function with a single map argument
such as {:name "Robert" :job "Engineer} .
This allows for easier programmatic invocation of functions with named parameters where you might be
building a map of parameters and passing it through your code.
Keyword default values are assigned by use of the :or keyword followed by a map of keywords to their
default value. Keywords not present and not given a default will be nil.
(0 2 4 6 8)
clojure.core has dozens of other higher-order functions. The most commonly used ones are covered
in clojure.core Overview (/articles/language/core_overview/).
Private Functions
Functions in Clojure can be private to their namespace.
27
(45 42 17)
"Joe"
Maps as Functions
Clojure maps are also functions that take keys and look up values for them:
"Joe"
42
nil
Note that this is not true for Clojure records, which are almost identical to maps in other cases.
Sets as Functions
(#{1 2 3} 1)
(#{1 2 3} 10)
nil
:uk
nil
Wrapping Up
Functions are at the heart of Clojure. They are defined using the defn macro, can have multiple arities,
be variadic and support parameter destructuring. Function arguments and return value can optionally be
type hinted.
Functions are first class values and can be passed to other functions (called Higher Order Functions or
HOFs). This is fundamental to functional programming techniques.
Several core data types behave like functions. When used reasonably, this can lead to more concise,
readable code.
Contributors
Michael Klishin [email protected] (mailto:[email protected]), 2012 (original author)
Links
About (/articles/about/)
Table of Contents (/articles/content/)
Getting Started (/articles/tutorials/getting_started/)
Introduction to Clojure (/articles/tutorials/introduction/)
Clojure Editors (/articles/tutorials/editors/)
Clojure Community (/articles/ecosystem/community/)
Basic Web Development (/articles/tutorials/basic_web_development/)
Language: Functions
Language: clojure.core (/articles/language/core_overview/)
Language: Collections and Sequences (/articles/language/collections_and_sequences/)
Language: Namespaces (/articles/language/namespaces/)
Language: Java Interop (/articles/language/interop/)
Language: Polymorphism (/articles/language/polymorphism/)
Language: Concurrency and Parallelism (/articles/language/concurrency_and_parallelism/)
Language: Macros (/articles/language/macros/)
Language: Laziness (/articles/language/laziness/)
Language: Glossary (/articles/language/glossary/)
Ecosystem: Library Development and Distribution (/articles/ecosystem/libraries_authoring/)
Ecosystem: Web Development (/articles/ecosystem/web_development/)
Ecosystem: Generating Documentation (/articles/ecosystem/generating_documentation/)
Building Projects: tools.build and the Clojure CLI (/articles/cookbooks/cli_build_projects/)
Data Structures (/articles/cookbooks/data_structures/)
Strings (/articles/cookbooks/strings/)
Mathematics with Clojure (/articles/cookbooks/math/)
Date and Time (/articles/cookbooks/date_and_time/)
Working with Files and Directories in Clojure (/articles/cookbooks/files_and_directories/)
Middleware in Clojure (/articles/cookbooks/middleware/)
Parsing XML in Clojure (/articles/cookbooks/parsing_xml_with_zippers/)
Growing a DSL with Clojure (/articles/cookbooks/growing_a_dsl_with_clojure/)