Marius Schulz
Marius Schulz
Front End Engineer

Playing with Closure Expressions and Operator Functions in Swift

Assume that we have the following array of numbers which we want to sort ascendingly:

let numbers = [8, 23, 4, 42, 16, 15]

We could use the sort function defined on the Array type, but I prefer not to do in-place updates of the numbers array for the sake of immutability. Because immutable collections can't change, they can't be sorted in-place. Instead, we're going to use the sorted function to retrieve a new array containing the sorted values.

The sorted function expects a comparison function as its single argument. It is used to compare two adjacent values and decide whether they're correctly ordered (returning true in that case) or not (returning false). We hand it to the sorted function as a closure expression in curly braces:

var sortedNumbers = numbers.sorted({
    (left: Int, right: Int) -> Bool in
    return left < right
})

println(sortedNumbers)
// [4, 8, 15, 16, 23, 42]

And indeed, it works: println(sortedNumbers) prints out [4, 8, 15, 16, 23, 42]. We can simplify our closure expression, though. Let's clean it up a little.

First, we can leave out the explicit types. The Swift compiler will infer those for us:

sortedNumbers = numbers.sorted({
    (left, right) in
    return left < right
})

We can also remove the return keyword because the closure body only contains a single expression. On top of that, the parentheses around the argument list are optional:

sortedNumbers = numbers.sorted({ left, right in left < right })

Ah, much better already. Now the code fits in one line. But we're not done yet, we can simplify it even further. If we don't list the arguments of the closure expression, Swift will automatically provide shorthand argument names based on their indices, such as $0, $1, or $2:

sortedNumbers = numbers.sorted({ $0 < $1 })

Note that shorthand argument names aren't dynamically typed, although it might look that way. They're statically typed, just like the rest of your code. The number of arguments and their respective types are all inferred and checked by the compiler.

Swift implements a little syntactic sugar called the trailing closure syntax. It allows us to write the closure expression after the function call to improve readability:

sortedNumbers = numbers.sorted() { $0 < $1 }

And because the closure expression is the only argument provided to the sorted function, we can even leave out the parentheses of the function call:

sortedNumbers = numbers.sorted { $0 < $1 }

This is as short as it gets using a closure expression. We don't have to define a new comparison function, though, because Swift already implements the < comparison operator that can be used like this:

sortedNumbers = numbers.sorted(<)

Why does this example compile? After all, < is an operator, right? In Swift, operators are implemented by providing a so-called operator function, which is a function like any other. It can therefore be passed as the single argument to the sorted function if it has a compatible signature — and it does!

Conversely, a > operator is implement that compares the other way around. Thus, sorting the list of values descendingly is as simple as that:

let descendingNumbers = numbers.sorted(>)

println(descendingNumbers)
// [42, 23, 16, 15, 8, 4]

Pretty neat, indeed!

If you want to see a few more examples of using operator functions, make sure to check out Passing Swift's Operator Functions to Higher-Order Functions as well.