First Class Functions and Delayed Evaluation in Swift

Functions are first-class citizens in Swift. If you're not familiar with the concept of first-class functions, this simply means that functions are data. An integer: Int and a function that returns an integer: () -> Int are both types of data. Either one can be stored in a variable or constant, passed to another function as a parameter, or returned as the result of another function. This concept of "functions as data" enables the development of complex systems composed of small bits of reusable logic in an elegant and concise way.

One of the ways functions can be composed is by wrapping one function in another function.

Let's say I've defined a really expensive calculation in my application.

func getOne() -> Int {
    return 1
}

This function will be useful in many places in my program. And since functions are just data I can easily pass it around to any other function or object that needs it.

It's such an expensive operation though, it would be silly to keep calculating it over and over again each time. Or worse, maybe the user didn't access the functionality that calculation supports and it's not even needed at all. It would be great if we could delay the evaluation of this function and then cache the result only if it's needed.

func delay<T>(f: () -> T) -> () -> T {
    var value: T?

    func delayed() -> T {
        if value == nil {
            value = f()
        }
        return value!
    }

    return delayed
}

This is another function (still data) that takes a function returning type T and returns a new function returning type T. But what is it doing?

var value: T?

First it defines a variable within the outer functions scope to hold the value of our calculation. It's defined as an optional because we don't want to perform our expensive calculation unless we have to. This variable will now be available within the scope of this and any enclosed functions.

func delayed() -> T {
    if value == nil {
        value = f()
    }
    return value!
}

Next it defines new function that returns a T. This function simply checks to see if we already have the value of our original function, calls it if we don't, and returns the unwrapped result.

return delayed

Finally it just returns our new function. A function in, a function out. And the best part is, because the function types match (() -> Int) we can use this new function anywhere we were using the original function without any other modifications to our code.

Just for fun, let's look at another way we can use our delay function. Swift likes to be concise after all.

var two = delay({ () -> Int in return 2 })

It doesn't have to stop with delay though. We can use our delay function to help compose new functions.

func numPlusOne(n: Int) -> () -> Int {
    return delay({ () -> Int in return n + 1 })
}

var three = numPlusOne(2)

for _ in 0..<100 {
    three() // 3 is returned 100 times but n + 1 is only calculated once
}

Here's an Xcode playground so you can play with this stuff if you want.

Stay classy, Swift functions.

"First Class Functions and Delayed Evaluation in Swift" was originally published on 30 Aug 2014.