Blog

Swift Tip: Mixing and Matching Imperative and Functional Code

Swift is a hybrid language: it knows all the typical imperative control flow statements and supports mutable variables and properties; at the same time, Swift also supports typical functional programming features, like value types and first class functions.

In Swift, we often have the choice: do we want to express a piece of logic in an imperative or in a functional way?

As a simple example, we’ll sum up the total width of an array of views:

var width: CGFloat = 0
for view in subviews {
    width += view.frame.width
}

This is a straightforwardly imperative way to express this logic. However, we could also choose the functional alternative:

let width = subviews.reduce(0) { result, view in
    result + view.frame.width
}

Both snippets do the same thing, with one small difference: in the imperative case we’re left with a mutable width variable after the calculation is done, whereas we have an immutable width variable in the functional case.

To make the code a bit more interesting, we’ll try summing up the total width of the views where isHidden is false. Once the logic becomes more complex, the alternative ways of expressing it start to diverge.

Here’s the imperative version:

var width: CGFloat = 0
for view in subviews {
    guard !view.isHidden else { continue }
    width += view.frame.width
}

Of course, there are other ways of expressing this logic imperatively, for example using a where clause:

for view in subviews where !view.isHidden {
    width += view.frame.width
}

Let’s try a functional version that accomplishes the same thing. One option is to first filter out the hidden views and then use reduce to calculate the total width:

let visibleViews = subviews.filter { !$0.isHidden }
let width = visibleViews.reduce(0) { result, view in
    result + view.frame.width
}

Alternatively, we can include the isHidden check within the reduce function:

let width = subviews.reduce(0) { result, view in
    view.isHidden ? result : result + view.frame.width
}

A nice feature of the previous version β€” first filtering, then reducing β€” is that the reduce part of the logic is the same, no matter if we sum up the width of all views, only the even views, or any other subset of them. This sameness is a clear suggestion that the logic can be abstracted out, for example by using a computed property:

extension Array where Element == UIView {
    var totalWidth: CGFloat {
        return reduce(0) { result, view in result + view.frame.width }
    }
}

Now, the total width of the even views is simply:

let width = visibleViews.totalWidth

Once again, the implementation of this extension and the extraction of the visible views could also be written imperatively:

extension Array where Element == UIView {
    var totalWidth: CGFloat {
        var result: CGFloat = 0
        for view in self {
            result += view.frame.width
        }
        return result
    }
}

var visibleViews: [UIView] = []
for view in subviews where !view.isHidden {
    visibleViews.append(view)
}
let width = evenViews.totalWidth

Swift allows mixing and matching of imperative and functional programming techniques, and often which technique you prefer is a matter of taste. We use both in our own code; for some problems we naturally use map or reduce, for other problems we write an imperative loop and aggregate the result as we go along.

No matter which you choose, breaking your problem down into smaller parts, as we have done with the totalWidth extension above, is a very useful technique for both.

If you’d like to learn more about functional approaches in Swift, our book Functional Swift is a great place to start πŸ™‚


Stay up-to-date with our newsletter or follow us on Twitter.

Back to the Blog

recent posts