0% found this document useful (0 votes)
10 views

Clojure Guides_ Language_ Functions

Uploaded by

eowug
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views

Clojure Guides_ Language_ Functions

Uploaded by

eowug
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 12

Language: Functions

This guide covers:

How to define functions


How to invoke functions
Multi-arity functions
Variadic functions
Higher order functions
Other topics related to 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).

What Version of Clojure Does This Guide Cover?


This guide covers Clojure 1.11.

Overview
Clojure is a functional programming language. Naturally, functions are very important part of Clojure.

How To Define Functions


Functions are typically defined using the defn (https://clojuredocs.org/clojure.core/defn) macro:

(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:

user=> (doc round)


user/round
([d precision])
Round down a double to the given precision (number of significant digits)
nil
In Clojure, function arguments may have optional type hints:

(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:

(require '[clojure.math :as math])


(defn round
[d precision]
(let [factor (math/pow 10 precision)]
(/ (math/floor (* d factor)) factor)))

Functions can also define preconditions and postconditions that put restrictions on argument values and
the value function returns:

(require '[clojure.math :as math])


(defn round
"Round down a double to the given precision (number of significant digits)"
[d precision]
{:pre [(not-nil? d) (not-nil? precision)]}
(let [factor (math/pow 10 precision)]
(/ (math/floor (* d factor)) factor)))

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:

(let [f (fn [x]


(* 2 x))]
(map f (range 0 10)))

(0 2 4 6 8 10 12 14 16 18)

There is also a reader macro for anonymous functions:

(let [f #(* 2 %)]


(map f (range 0 10)))

(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:

;; an anonymous function that takes 3 arguments and adds them together


(let [f #(+ %1 %2 %3)]
(f 1 2 3))

Please use this reader macro sparingly; excessive use may lead to unreadable code.

How To Invoke Functions


Functions are invoked by placing a function in the leading position (the calling position) of a list:

(format "Hello, %s" "world")

"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"

Alternatively, you can call a function using clojure.core/apply (https://clojuredocs.org/clojure.core/apply)


(apply format "Hello, %s" ["world"])

"Hello, world"

(apply format "Hello, %s %s" ["Clojure" "world"])

"Hello, Clojure 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:

(require '[clojure.math :as math])


(defn tax-amount
([amount]
(tax-amount amount 35))
([amount rate]
(math/round (double (* amount (/ rate 100))))))

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)))

Destructuring of Function Arguments


Sometimes function arguments are data structures: vectors, sequences, maps. To access parts of such
data structure, you may do something like this:
(defn currency-of
[m]
(let [currency (get m :currency)]
currency))

For vector arguments:

(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)

Destructuring can nest (destructure deeper than one level):

(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:

;; instead of {:currency "GBP" :amount 95.99} let's assume we have namespaced


;; keys: {:invoice/currency "GBP" :invoice/amount 95.99}
(defn currency-of
[{:keys [invoice/currency invoice/amount]}] ; prefixed names
currency) ; bind to unprefixed symbols

;; 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:

(let [money {:currency :gbp :amount 10}


{currency :currency} money]
currency)

: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 :

(str "a" "b")

"ab"

(str "a" "b" "c")

"abc"
(format "Hello, %s" "world")

"Hello, world"

(format "Hello, %s %s" "Clojure" "world")

"Hello, Clojure world"

To define a variadic function, prefix optional arguments with an ampersand ( & ):

(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

(log "message from " "192.0.0.76" "service:xyz")

nil

As you can see, optional arguments ( args ) are packed into a list.

Extra Arguments (aka Named Parameters)


Named parameters are achieved through the use of destructuring a variadic function.

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

Using the function looks like this:


(job-info :name "Robert" :job "Engineer")

["Robert" "Engineer" "$0.00"]

(job-info :job "Engineer")

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} .

As of Clojure 1.11, you can also pass named parameters as a map:

(job-info {:name "Robert" :job "Engineer"})


;;=> ["Robert" "Engineer" "$0.00"]

(job-info {}:job "Engineer"})


;;=> No name specified

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.

Higher Order Functions


Higher-order functions (HOFs) are functions that take other functions as arguments. HOFs are an
important functional programming technique and are quite commonly used in Clojure. One example of an
HOF is a function that takes a function and a collection and returns a collection of elements that satisfy a
condition (a predicate). In Clojure, this function is called clojure.core/filter :

(filter even? (range 0 10)) ; ⇒ (0 2 4 6 8)

(0 2 4 6 8)

In the example above, clojure.core/filter takes clojure.core/even? as an argument.

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.

They are covered in more detail in the Namespaces (/articles/language/namespaces/) guide.


Keywords as Functions
In Clojure, keywords can be used as functions. They take a map or record and look themselves up in it:

(:age {:age 27 :name "Michael"})

27

This is commonly used with higher order functions:

(map :age [{:age 45 :name "Joe"}


{:age 42 :name "Jill"}
{:age 17 :name "Matt"}])

(45 42 17)

and the -> macro:

(-> [{:age 45 :name "Joe"} {:age 42 :name "Jill"}]


first
:name)

"Joe"

Maps as Functions
Clojure maps are also functions that take keys and look up values for them:

({:age 42 :name "Joe"} :name)

"Joe"

({:age 42 :name "Joe"} :age)

42

({:age 42 :name "Joe"} :unknown)

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

(#{:us :au :ru :uk} :uk)

:uk

(#{:us :au :ru :uk} :cn)

nil

This is often used to check if a value is in a set:

(when (countries :in)


(comment ...))

(if (countries :in)


(comment Implement positive case)
(comment Implement negative case))

because everything but false and nil evaluates to true in Clojure.

Clojure Functions As Comparators


Clojure functions implement the java.util.Comparator
(https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Comparator.html) interface and can
be used as comparators.

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)

« Basic Web Development (/articles/tutorials/basic_web_development/) || Language: clojure.core »


(/articles/language/core_overview/)

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/)

Copyright © 2024 Multiple Authors


Powered by Cryogen (https://cryogenweb.org)

You might also like