Bringing Closure
Functions are great, and in the earlier code you’ve written, you can see just how versatile they can be for encapsulating functionality and ideas. Although the many contrived examples you went through may not give you a full appreciation of how useful they can be in every scenario, that will change as you proceed through the book. Functions are going to appear over and over again both here and in your coding, so understand them well. You may want to re-read this chapter to retain all the ins and outs of functions.
We’ve got a little more to talk about before we close this chapter, however. Our tour of functions would not be complete without talking about another significant and related feature of functions in Swift: closures.
In layman’s terms, a closure is essentially a block of code, like a function, which “closes in” or “encapsulates” all the “state” around it. All variables and constants declared and defined before a closure are “captured” in that closure. In essence, a closure preserves the state of the program at the point that it is created.
Computer science folk have another word for closures: lambdas. In fact, the very notion of the function you have been working with throughout this chapter is actually a special case of a closure—a function is a closure with a name.
So if functions are actually special types of closures, then why use closures? It’s a fair question, and the answer can be summed up this way: Closures allow you to write simple and quick code blocks that can be passed around just like functions, but without the overhead of naming them.
In essence, they are anonymous blocks of executable code.
Swift closures have the following structure:
{ (parameters) -> return_type in statements }
This almost looks like a function, except that the keyword func and the name is missing; the curly braces encompass the entire closure; and the keyword in follows the return type.
Let’s see closures in action. Figure 4.22 shows a closure being defined on lines 196 through 201. The closure is being assigned to a constant named simpleInterestCalculationClosure. The closure takes three parameters: loanAmount, interestRate (both Double types), and years (an Int type). The code computes the future value of a loan over the term and returns it as a Double.
// Closures
let simpleInterestCalculationClosure = { (loanAmount : Double, var interestRate : Double, years : Int) -> Double in
interestRate = interestRate / 100.0
var interest = Double(years) * interestRate * loanAmount
return loanAmount + interest
}
func loanCalculator(loanAmount : Double, interestRate : Double, years : Int, calculator : (Double, Double, Int) -> Double) -> Double {
let totalPayout = calculator(loanAmount, interestRate, years)
return totalPayout
}
var simple = loanCalculator(10_000, 3.875, 5, simpleInterestCalculationClosure)
Figure 4.22 Using a closure to compute simple interest
The formula for simple interest calculation is:
futureValue = presentValue * interestRate * years
Lines 203 through 206 contain the function loanCalculator, which takes four parameters: The same three that the closure takes, and an additional parameter, calculator, which is a closure that takes two Double types and an Int type and returns a Double type. Not coincidentally, this is the same parameter and return type signature as our previously defined closure.
On line 208, the function is called with four parameters. The fourth parameter is the constant simpleInterestCalculationClosure, which will be used by the function to compute the total loan amount.
This example becomes more interesting when you create a second closure to pass to the loanCalculator function. Since you’ve computed simple interest, then write a closure that computes the future value of money using the compound interest formula:
futureValue = presentValue (1 + interestRate)years
Figure 4.23 shows the compound interest calculation closure defined on lines 210 through 215, which takes the exact same parameters as the simple calculation closure on line 196. On line 217, the loanCalculator function is again called with the same parameters as before, except the compoundInterestCalculationClosure is passed as the fourth parameter. As you can see in the Results sidebar, compound interest yields a higher future value of the loan than simple interest does.
let compoundInterestCalculationClosure = { (loanAmount : Double, var interestRate : Double, years : Int) -> Double in
interestRate = interestRate / 100.0
var compoundMultiplier = pow(1.0 + interestRate, Double(years))
return loanAmount * compoundMultiplier
}
var compound = loanCalculator(10_000, 3.875, 5, compoundInterest CalculationClosure)
Figure 4.23 Adding a second closure that computes compound interest
On line 212 you may notice something new: a reference to a function named pow. This is the power function, and is part of Swift’s math package. The function takes two Double parameters: the value to be raised and the power to raise it to. It returns the result as a Double value.