First Class Functions in Swift

A higher-order function is simply a function that takes another function as an argument.1 Swift uses HOFs all over the place. In fact, it considers the practice important enough to warrant language-level feature support in the form of trailing closure syntax:

// Takes a function as «completion» argument
func foo(with: String, completion: ()->Void) {...}
foo("bar") {
  // trailing closure syntax let's us create that
  // funcion in-line with a closure, packages it up 
  // and passes it in as «completion».
}

But the pervasive use of trailing closure syntax in Swift can lead us to believe that, when a parameter has a function type, we have to pass it a closure. Not true! In Swift, functions are first-class citizens. We can use a function, itself, anywhere we might otherwise use a closure or variable.

For example, to sum an array of numbers, we might be tempted to give the implementation in a trailing closure like so:

[1,2,3,4,5].reduce(0) { $0 + $1 } //> 15

This is just a fancy way of writing:

[1,2,3,4,5].reduce(0, combine: { a, b in
  return a + b
})

Looking at it this way, it’s clear all we’re really doing is passing the combine parameter an ad-hoc closure that takes two arguments and sums them together. But do we need to create an inline closure for this? There’s already an existing function that takes two arguments and sums them together. It’s called +. We should just use that:

[1,2,3,4,5].reduce(0, combine: +) //> 15

Treating functions as first-class citizens extends well beyond just passing them as parameters. We can assign them to variables:

floor(5.12345) //> 5
let myFunc: (Double)->Double = floor
myFunc(5.12345) //> 5

We can conditionally assign functions:

let roundUp = true
let myFunc: (Double)->Double = roundUp ? ceil : floor
myFunc(5.12345) //> 6

And, in fact, we don’t even need to assign functions to a variable to use them:

let roundUp = true
(roundUp ? ceil : floor)(5.12345) //> 6

Which is pretty cool. Anytime we use functions directly instead of wrapping them in closures or other forms of indirection, we increase the declarativeness of our code.


1: AKA a procedural parameter, which I am motivated to mention only owing to my love of alliteration.↩︎