(Swift) Closures Syntax
Closures are one of the most important topics in Swift language. In this article, I will try to explain its syntax in a short and brief way.
1. What are closures?
According to Apple’s official documentation, “Closures are self-contained blocks of functionality that can be passed around and used in your code.”. Many people say that you can think of it as a function without a name.
2. Function types
Before you learn about closures, you need to know that all functions have a specific type. That’s made up of the function’s parameter and return types. Let’s take a look at the function below.
func multiply(num1: Int, num2: Int) -> Int {
return num1 * num2
}
This function takes 2 Int values as parameters and returns Int. Thus, the function type for this functions is “(Int, Int) -> Int”.
3. Closure syntax
Closure syntax follows the general form below. The keyword “in” is just used to separate the closure’s parameters and return type from the statements. Don’t be confused with for-in loop’s in.
{ (parameters) -> returnType in
statements
}
Following the general form above, let’s create a simple closure and store it into a constant.
// parameters: (num1, num2)
// in: separator of parameters/return type and statements
// statements: return num1 * num2let multiply: (Int, Int) -> Int = { (num1, num2) -> Int in
return num1 * num2
}
// Calling the closure
multiply(2, 3) // will return 6
Notice “(Int, Int) -> Int” is a function type. Therefore, the constant “multiply” will be a type of “(Int, Int) -> Int” meaning it will work like a function which takes 2 Int arguments and returns Int value. The statements inside of the curly braces {} just define what to do with the arguments and what to return.
You can even write like below using shorthand syntax.
// Shorthand version 1
let multiply: (Int, Int) -> Int = { num1, num2 in num1 * num2 }// Shorthand version 2
let multiply: (Int, Int) -> Int = { $0 * $1 }
What’s going on, you ask? I will explain this syntax in detail.
4. Shorthand Syntax
Usually, you rarely see closures in their general form because people use shorthand syntax. Using built-in sorted(by:) function as an example, I will show you how to make the closures shorter.
// General form
var oneToTen = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
oneToTen.sorted(by: { (num1: Int, num2: Int) -> Bool in
return num1 > num2
})
This is just an example of the general form of closure expression. num1 and num2 are just arguments which are the type of Int. Comparing these two arguments, the closure finally returns the Bool value. Nothing special about this syntax.
Type inference
oneToTen.sorted(by: { num1, num2 in num1 > num2 })
This is a little bit shorter version. There are 3 changes compared with the general form.
- Now the whole closure is in one line.
This is just to make it look cleaner. - The parameter type and return type have been removed.
Taking advantage of Swift’s type inference, these types can be removed. - The “return” keyword has been removed.
This is possible just because there is only one expression (num1 > num2). If there were multiple lines of expression, the “return” keyword would be necessary.
Shorthand argument names
oneToTen.sorted(by: { $0 > $1 })
This one looks totally different from the general form. Although this form is kind of tricky, you will see this “$” symbols a lot in the real world. Here is what happened to the closure.
- num1 and num2 are replaced by $0 and $1
According to Apple’s official document, “Swift automatically provides shorthand argument names to inline closures, which can be used to refer to the values of the closure’s arguments by the names $0, $1, $2, and so on.”.
In the example above, the compiler knows sorted(by:) method takes two integer values since oneToTen array is Int type. In addition, sorted(by:) will return Bool value by its nature.
In other words, the compiler knows that sorted(by:) is taking two integer values and returning Bool value by comparing those exact two arguments. Type inference capabilities of the compiler help it to know the number and types of the arguments that the closure uses in its statement. Therefore, we don’t have to name the arguments which means we can take advantage of Swift’s shorthand arguments names.
Since we don’t have to name them, we could remove “num1, num2” from the previous form and change the “num1 > num2” to “$0 > $1”. That’s what happened here.
NOTE:
$0 and $1 are index numbers. Like the indices of an array, these order matters. Although you can have as many “$” as you want, you cannot skip the number.
The shortest form
// Shortest form
oneToTen.sorted { $0 > $1 }
This would be the shortest form of closures. There is one difference between the normal shorthand form and this form.
- The parentheses () and parameter name “by:” have been removed.
When a closure is passed to a function’s final argument, it can be written outside of the function’s parentheses. Since sorted(by:) takes only one argument, we can omit the parentheses along with the parameter name “by:”.
Closures Are Reference Type
I think that was enough for the syntax. However, there is still one more important feature related to closures that closures are reference type. If you store a closure into a variable or constant and assign it to another variable or constant, the second one points to exactly the same closure as the first one. That means values inside of the closure will be shared among these two. Let’s take a look at the example below.
var createIncrementer: (Int) -> () -> Int = { num in
var result = 0
func incrementer() -> Int {
result += num
return result
}
return incrementer
}let incrementTen = createIncrementer(10)
let incrementTen2 = incrementTenincrementTen() // result is 10
incrementTen() // result is 20
incrementTen() // result is 30
incrementTen2() // result is 40
The “createIncrementer” is a simple closure which takes an argument in Int, and returns another function called “incrementer”. The “incrementer” takes no arguments and returns the Int value which is the sum of the variable “result” and the argument “num”. This is how you read this part “(Int) -> ( ) -> Int”.
The “incrementTen” constant is made to increment the closure’s value by ten. Since the “incrementTen” constant is passed into the “incrementTen2” constant, it points to exactly the same closure as “incrementTen”. Whenever these constants are called, it will increment the same value as a result.
For more details about this topic, you can go to “The Swift Programming Language Guide — Capturing Values and Closures Are Reference Type sections”.
That’s everything for this article. If you have any comments or questions, please feel free to leave a post. Thank you for reading.
Reference
Functional swift: All about Closures
https://medium.com/@abhimuralidharan/functional-swift-all-about-closures-310bc8af31dd
The Swift Programming Language Guide Swift 4.2 Closures
https://docs.swift.org/swift-book/LanguageGuide/Closures.html#//apple_ref/doc/uid/TP40014097-CH11-ID546
Apple Official Developer Document
https://developer.apple.com/documentation/swift/array/2296815-sorted
Swift Programming: The Big Nerd Ranch Guide (2nd Edition)
https://www.bignerdranch.com/books/swift-programming/