Swift Overview - Closures

Closures are similar to lambdas in Python or Ruby. They are self contained chunks of code.

Global and nested functions are closures, but are often not referred to as such. Most times when people refer to a closure in Swift they are talking about a closure expression.

A closure expression is an unnamed, self contained block of code that can be passed as an argument to a function, they are unique in that they are unnamed, but operate exactly like a function.

They are used to specify an action to be executed some time in the future.

Syntax

{ (parameters) -> return type in
  Statements to execute
}

Using in Keyword

In a named function, we declare the parameters and return type in the func declaration line.

func sayHello(str:String)->() {  
    // body
}

In an anonymous function, there is no func declaration line because it's anonymous. So we do it with an in line at the start of the body instead.

{ (str:String)->() in
    // Statements to execute
}

stackoverflow

Example Sorting Doubles

Here we are passing a closure to the sorted method.

var bids = [48.1, 75.4, 63.7, 52.4, 68.2]  
var orderedBids = bids.sorted(by: { (bid1: Double, bid2:Double) -> Bool in  
  return bid2 > bid1
})

Shorthand

var examGrades = [81, 99, 54, 84, 98]  
var passingGrades = examGrades.filter({(grade: Int) -> Bool in  
  return grade > 70
})

The compiler can infer closure expression types based on what parameter type a given function expects. In this case the compiler can infer that the closure will take in a parameter that matches the type of the array being filtered, and can also infer that this closure will return a bool. So we can leave off the words Int and Bool

var passingGrades = examGrades.filter({grade  in  
  return grade > 70
})

If a closure is a single expression it can be inferred that the result of that expression should be returned so we can remove the word return

var passingGrades = examGrades.filter({grade  in  
  grade > 70
})

Swift provide default argument names for closure expressions. So instead of specifying a first argument, the first argument is referred to as $0 and the second is referred to as $1 and so on. So here the compiler can infer that the closure expression takes in one argument and that its type Int, matches the type of the elements in the array and can refer to the argument as $0, and since we are not giving the argument a name we can take out the word in

var passingGrades = examGrades.filter({ $0 > 70 })  

Closures Are First-Class Types

Closures are treated as first class citizens because they can

  • be assigned to variables and constants
  • be added to arrays or dictionaries
  • return from functions or other closures
  • receive them as parameters of functions and closures
// Assign to a var or constant
let f = { (x:Int) -> Int in  
    return x + 42
}
// or

let f: (Int) -> Int = { $0 + 42 }

f(5) // output: 47  
let closures = [  
    f, // our previous closure
    {(x:Int) -> Int in return x * 2}, // a new Int -> Int closure
    {x in return x - 8}, // no need for the type of the closure!
    {(x:Int) in x * x}, // no need for return if only one line
    {$0 * 42} // access parameter by position instead of name
]

When we store closures in a collection type like an array the first closures signature needs to be used for all of the other closures in the collection. In the above example the first one takes and Int and returns and Int so all others much do the same.

We can call the closures stored in the dictionary. One way to do this is to iterate through our array and call each one. Similarly we could index into the array and call them by their position in the array.

for fn in closures {  
    fn(42) output: 84, 34, 1764, 1764
}

closures[0](42) // output: 84  

Functions vs. Closures

Although functions and closures look different and have some nuances when stored in collections under the hood they are not just similar they are exactly the same thing. To the compiler the following is exactly the same.

func foo(x: Int) -> Int {  
    return 42 + x
}

let bar = {(x: Int) -> Int in 42 + x }

Typealias

An alias is a simple way of calling something that has a complex or strange name.

In swift you can give new names to existing objects that have complex names or look confusing to you. This will not alter the ability to call the previous name, rather just extends the nomenclature.

typealias Integer = Int

let z: Integer = 42  
let zz: Int = 42

typealias IntToInt = (Int)->Int  
typealias IntMaker = (Void)->Int  

Variable Capture

Variable capture, or more technically, capture of lexical environment.

func makeCounter() -> IntMaker {  
    var n = 0

    // Functions within functions ;)
    func adder() -> Integer {
        n = n + 1
        return n 
    }
    return adder
}

Above we use func syntax for the makeCounter simply because the syntax is more readable in this context to me.

In the above code the inner function does not take any arguments but has access to the variable n in the outer scope and is actually modifying it's value when run.

So if we call the function a few times in a row the output will be as follows.

let counter1 = makeCounter()  
let counter2 = makeCounter()

counter1() // output: 1  
counter1() // output: 2  
counter1() // output: 3

counter2() //output: 1  

Note calling the instance of counter1 multiple times does not have any affect the output or variables in the instance of counter2. Each closure we return by calling makeCounter takes it's own copy of all the captured variables, so it has it's own replica of the environment in which it was created.

Features like closures being treated as first class citizens, and variable capture are the foundations of functional programming, and one of the reasons that makes Swift such a powerful language.