Functions as values
What you'll learn:
- store a function in a variable, pass it as an argument and return it from another function;
- write closures briefly: without types, with
$0and after the call's parentheses; - process arrays with the
map,filter,reduceandcompactMapmethods; - explain how a closure captures a variable and why two constants can share one counter.

Picture a recipe card. You can cook from it right now. Or you can hand the card to a friend, put it in a box of recipes or stick it on the fridge. Whoever ends up holding it cooks from it, whenever they need to.
In the functions theme you called a function at once: you wrote its name, the parentheses and the arguments. But a function can also be treated like a card. The function's name without parentheses is the card itself, and the parentheses after the name mean "cook it now".
Here is a function stored in a variable:
func double(_ x: Int) -> Int {
x * 2
}
func square(_ x: Int) -> Int {
x * x
}
var transform: (Int) -> Int = double
print(transform(5)) // 10
transform = square
print(transform(5)) // 25Let's go through the program step by step:
- The functions
doubleandsquaretake anIntand return anInt. var transform: (Int) -> Int = doubleputs the functiondoubleitself into the variable. There are no parentheses afterdouble, so the function is not called.transform(5)calls whatever the variable holds and prints10.transform = squareputs another function into the same variable.- The same call
transform(5)now prints25.
A function type
Every value in Swift has a type, and so does a function. A function type is written like this: the parameter types in parentheses, an arrow and the result type. (Int) -> Int has three parts:
(Int)— the function takes one integer;->— the arrow, as in a function's header;Int— the function returns an integer.
double and square do different things, but they have the same type, so both fit into transform. Here are three more types:
(Int, Int) -> Int— the function takes two numbers and returns a number;(String) -> Void— the function takes a string and returns nothing;() -> Int— the function takes no parameters and returns a number.
Argument labels are not part of the type. If you put the function add(a:b:) into a constant op of type (Int, Int) -> Int, you call it without labels: op(2, 3). The call op(a: 2, b: 3) does not compile.
A variable holds only a function of a matching type. A function that takes a string does not fit into transform: the compiler reports that the types do not match.
A function as a parameter
Since a function is a value, you can pass it to another function as an argument. It is like handing the card to a friend: they cook from your recipe as many times as they need.
func applyTwice(_ f: (Int) -> Int, to x: Int) -> Int {
f(f(x))
}
print(applyTwice(double, to: 3)) // 12
print(applyTwice(square, to: 3)) // 81Let's go through the first call:
applyTwicereceives the functiondoublein the parameterfand the number3in the parameterx.- The inner call
f(x)isdouble(3), which gives6. - The outer call
f(6)gives12, and the program prints it.
In the second call f holds square: first 3 * 3, then 9 * 9, that is 81.
A function that takes another function or returns one is called a higher-order function. Two parties share the work here: applyTwice decides how many times and with which number to call f, and the function passed in decides what to do with that number.
A function as a result
A function can also be returned from another function. Then the header has a function type after the arrow:
func pick(big: Bool) -> (Int) -> Int {
if big {
return square
}
return double
}
let chosen = pick(big: true)
print(chosen(4)) // 16
print(pick(big: false)(4)) // 8The header func pick(big: Bool) -> (Int) -> Int reads like this: the function pick takes a Bool and returns a function of type (Int) -> Int. Let's go through the calls:
pick(big: true)returns not a number but the functionsquare, and it goes into the constantchosen.chosen(4)callssquareand prints16.pick(big: false)(4)first gets the functiondouble, and the second pair of parentheses calls it with the number4right away. The result is8.
Interviewers often say it like this: in Swift, functions are first-class values. It means you can do with a function what you do with a number or a string:
- put it into a variable or a constant;
- pass it as an argument to another function;
- return it from a function.
Here the recipe-card picture stops working. Once you hand over a paper card, you no longer have it. A function can sit in ten variables at once, and they all lead to the same function.
Common mistake
The function was passed with parentheses:
print(applyTwice(double(3), to: 3)) // ✗error: cannot convert value of type 'Int' to expected argument type '(Int) -> Int'The program does not compile. The parentheses after double call the function on the spot, and double(3) gives the number 6. But the parameter f expects a function of type (Int) -> Int, not an Int. It is like handing your friend not the card but a dish that is already cooked. Pass the name without parentheses: applyTwice(double, to: 3).
Key points
- A function is a value: you store it in a variable, pass it as an argument and return it from another function.
- A function type is written as
(Int) -> Int: the parameter types in parentheses, an arrow and the result type; argument labels are not part of the type. - A higher-order function takes another function or returns one.
- A name without parentheses is the function itself; a name with parentheses is its call and its result.