Variations in Closure Expression Syntax - ADDITIONAL TOPICS - Understanding Swift Programming: Swift 2 (2015)

Understanding Swift Programming: Swift 2 (2015)

PART 3: ADDITIONAL TOPICS

23. Variations in Closure Expression Syntax

In an earlier chapter (Chapter 18 on Closures and Closure Expressions) I discussed how closures, functions, and closure expressions were related and how closure expressions were used.

You saw the full closure expression syntax:

var d = { (a: Int, b: Int) -> Int in

var c = a + b

return c

}

var m = d(3, 4)

print (m) // Prints: 7

As well as the simplest:

var a = { print("Hello") }

a() // Prints: Hello

And this illustration that describes the syntax:

I said at that time that closures were very flexible in allowing a wide variety of syntax variations. The purpose of this chapter is to describe these variations and provide some hint about how the Swift compiler goes about accepting these variations.

Apple has put a lot of effort into a compiler that is quite intelligent in the way that it parses expressions for closures, and allows the programmer a lot of latitude.

This is mostly, but not necessarily entirely, a good thing. Even though it is possible to leave out much or even nearly all of the syntax for a particular closure, you should think about whether you should. Will the resulting code be readable? What makes perfect sense (and saves some typing) when you are doing a lot of coding with closures may not make much sense six months later when you have been working on something else for some time. Not to mention co-workers who may not have your level of skill in Swift who may end up working on your code.

The standard example—used in the Apple documentation—for describing the flexibility of Swift closure expression syntax is based on the Swift sorting function. This is an interesting example because it not only demonstrates how to get from the full closure syntax to the minimal form, but to an unusually freakish minimum of only a single character, a "<", telling the sort function what order it should sort things in.

Sorting An Array with a Closure Expression with Full Syntax

We begin with an array of strings that each represent the name of one of five states:

var states = ["Delaware","Alaska","Iowa","Florida","Connecticut"]

And we want to use a global Swift function known as sorted to sort them. The function sorted is a slightly more complex version of sort, which allows more control over how the sorting is done. The sort function just accepts the array to be sorted as its only input value. Thesorted function accepts the array to be sorted, plus a second input parameter that is designed for either a closure expression or a function.

The code to sort with a closure expression is:

var statesSorted = sorted(states,

{ (item1: String, item2: String) -> Bool in

return item1>item2

})

The global function sorted has two input parameters. The first is states, which is the array we just assigned that has names of states. The second argument is a closure expression. The closure expression is surrounded by the usual braces and begins with input parameters:

(item1: String, item2: String)

As we saw from the earlier chapter, it can get a little confusing when a function has what seems to be an "input parameter", but then that parameter turns out to be a closure or function, that itself has inputs and outputs. So this "input parameter" actually puts out values? Well, no. But the closure that gets supplied to the function sorted has inputs and outputs, and the function sorted does supply values to the closure.

The values that the function supplies to the closure are a pair of strings in a particular order. And what it wants in response is a simple Boolean value that indicates whether the strings have been provided in the correct order (true) or not (false).

Whenever the sorting function needs guidance about what order to sort (how it does this and how many times it does it depends on the sorting algorithms the function uses internally) it calls the closure expression with an ordered pair of strings (the names of the states, presumably) and then looks at the Boolean value of the output.

The closure expression has the expected return arrow (->), return type (Bool), and in keyword.

It also has a return statement that has the expression item1>item2. The > operator is actually the critical thing here—how it works in comparing whether one string is "greater than" another is what determines how the sorting is done.

When I ran this code I got the following:

"Iowa", "Florida", "Delaware", "Connecticut", "Alaska"

So assuming that we want alphabetical order, our sorting works but it is backwards. So we change the "greater than" character to a "less than" symbol:

var statesSorted = sorted(states,

{ (item1:String,item2:String) -> Bool in

return item1<item2

})

Which yielded:

"Alaska", "Connecticut", "Delaware", "Florida", "Iowa"

The desired result.

Successively Simplifying the Syntax

We can now go through a succession of simplifications of the syntax.

Input Parameter Types Not Required. In general, when providing a closure as a parameter in a function, it is not required to indicate the type. The function knows what type it will accept; the compiler can infer the type from what is returned (at least at this stage.) We can thus drop the input parameter types:

states = ["Delaware","Alaska","Iowa","Florida","Connecticut"]

statesSorted = sorted(states,

{ (item1,item2) -> Bool in

return item1<item2

})

Input Parameter Parentheses Not Required. The parentheses around the parameters are also not required when providing a parameter in a function. Given the remainder of the closure expression, it is clear to the compiler what is going on, and the parentheses around the input parameters can be dropped. Thus, the following is correct syntax:

states = ["Delaware","Alaska","Iowa","Florida","Connecticut"]

statesSorted = sorted(states, {

item1,item2 -> Bool in

return item1<item2

})

Return Keyword Not Required if Single Expression. If a closure contains only a single expression, and a type is defined for a return value, then there is no need for a return keyword, because it is clear that a return value must be provided and it is clear which expression (there being only one) should be executed to calculate such a return value. Thus, the following is correct:

states = ["Delaware","Alaska","Iowa","Florida","Connecticut"]

statesSorted = sorted(states, {

item1,item2 -> Bool in

item1<item2

})

Trailing Closure Syntax. Normally, the input parameters for a function are contained within a pair of parentheses. However, in the special case in which an input parameter comes last (that is, is the “trailing” parameter), there is an alternative syntax allowed: The right (closing) parenthesis comes just at the end of the next-to-last input parameter states, and the left brace of the closure comes just after the closing parenthesis. (Blank spaces are allowed after the parenthesis). Thus, the following is correct syntax:

states = ["Delaware","Alaska","Iowa","Florida","Connecticut"]

statesSorted = sorted(states) { item1,item2 -> Bool in

item1<item2

}

Some functions (not this one) have only a single parameter, that for the closure, and thus the parentheses with a trailing closure would be empty. In such a case, the parentheses can be dropped.

Return Arrow and Type Not Required. It's clear from the input type expected by the sorted function that a Bool should be returned. We can drop the return arrow and the type and the compiler will figure it out:

states = ["Delaware","Alaska","Iowa","Florida","Connecticut"]

statesSorted = sorted(states) { item1,item2 in

item1<item2

}

Shorthand Input Parameters and Dropping in Keyword. Rather than providing a name for each input parameter that is then used in the closure statements to refer to a particular input parameter, it is permissible to use instead a shorthand symbol to refer to each input parameter. The first input parameter is referred to by the symbol $0, while the second input parameter is referred to by the symbol $1, etc.). (This is also called an automatic argument name.) We can at the same time drop the in keyword. Thus, the following is correct syntax:

states = ["Delaware","Alaska","Iowa","Florida","Connecticut"]

statesSorted = sorted(states) { $0<$1 }

Dropping Everything But a Single Comparison Operator. In the ultimate example of simplification, we drop the trailing closure syntax and revert to the conventional syntax, but pass only a single comparison operator to the function.

states = ["Delaware","Alaska","Iowa","Florida","Connecticut"]

statesSorted = sorted(states,<)

Hands-On Exercises

Go to the following web address with a Macintosh or Windows PC to do the Hands-On Exercises.

For Chapter 23 exercises, go to

understandingswiftprogramming.com/23