Swift, more elegant code: Autoclosure

Ahmad Fayyas
5 min readApr 25, 2018

--

So far you are familiar with the definition of “closure” and how basically work with it, but you feel that you could expand your familiarity by knowing what else can be done using closures, well this article is for you!

Personally, I find closures are one of the coolest features in Swift, I believe working with closures makes the code to be more expressive, responsive and easy to deal with. Even standard functions are considered as special closures (named closures).

What is autoclosure?

For the first time I read about the autoclosure, I found it a little confusing to understand the purpose of using it, until I figured that there is no need to overthink about it, it is really simple.

Apple defines the autoclosure as:

An autoclosure is a closure that is automatically created to wrap an expression that’s being passed as an argument to a function. It doesn’t take any arguments, and when it’s called, it returns the value of the expression that’s wrapped inside of it. This syntactic convenience lets you omit braces around a function’s parameter by writing a normal expression instead of an explicit closure.

Actually it is nothing more than a syntactic sugar for passing a closure as an argument; When passing an autoclosure as a function parameter, you don’t have to type the curly braces!

How to implement autoclosure?

Actually there is nothing to do for the closure itself that you want to pass it as a parameter, it is related to the function that takes the closure as a parameter; By marking the closure parameter as @autoclosure:

func perform(closure: @autoclosure ()->())

You are telling the compiler that closure is an autoclosure, simple as that!

Why to use autoclosure?

As mentioned above -in “What is autoclosure?”-, for some cases, it becomes more handy to pass a closure as a parameter without the curly braces thing; I think one of the best places for using an autoclosure is operators.

Example:

For the purpose of making it more clear, let’s walk through this case:

Imagine that we are working on a part of a product related to organizing HR system, containing Employee struct as:

struct Employee {
var id: Int
var name: String
var salary: UInt
}

and new requirement has been requested as:

we need to create an operator as (-!) that decrements the employee salary, it should be used like this:

Employee -! amount

amount would be calculated based on many factors. However if the salary of the employee is less than 1000, nothing should happen

Pretty cool! Let’s call our new (-!)operator “decrement operator”. So, we will implement it as:

infix operator -!
extension Employee {
static func -!(lhs: inout Employee, rhs: ()-> UInt) {
if lhs.salary > 1000 {
lhs.salary -= rhs()
}
}
}

Based on the mentioned requirement, our implementation for the decrement operator seems to be fine. However, when trying to use it, we will notice that an “ugly” code has to be written 😥:

var jack = Employee(id: 101, name: "Jack Thompson", salary: 2000)
jack -! {100} // what are these curly braces!
print(jack)
// Employee(id: 101, name: "Jack Thompson", salary: 1900)

Although it works as expected, the necessity of typing “{}” is inappropriate, how can we get rid of it?! Well, that’s exactly the purpose of marking a closure as @autoclosure. All we should do is to change the decrement operator method signature to be:

static func -!(lhs: inout Employee, rhs: @autoclosure ()-> UInt)

thus we will be able to:

jack -! 100

Our decrement operator is more natural to be used now!

Sidebar tip:

Referring to how we implemented the decrement operator, you might be wondering:

Why do we need to declare the right hand side as (@autoclosure ()-> UInt) instead of directly (UInt)?

by doing it, we can implement our operator as:

static func -!(lhs: inout Employee, rhs: UInt) {
if lhs.salary > 1000 {
lhs.salary -= rhs
}
}

implies that:

jack -! 100

should work fine.

Good question! 👍

The reason behind it is closures are lazily evaluated; In our case, the right hand side needs to be evaluated only if the employee salary is more than 1000, so declaring rhs as UInt instead of @autoclosure ()-> UInt, getAmountToDecrmement() leads to let it always be evaluated even if the employee salary is less than 1000!

You don’t believe it?! Let’s give it a try by checking the log for both cases:

  • In case of declaring the decrement operator as static func —! (lhs: inout Employee, rhs: UInt) :
func getAmountToDecrmement() -> UInt {
print("consider there is a lot to be calculated here…")
return 101
}
var erik = Employee(id: 102, name: "Erik Smith", salary: 500)
erik -! getAmountToDecrmement()
print(erik)
// Employee(id: 102, name: "Erik Smith", salary: 500)

At this point, it would log:

consider there is a lot to be calculated here…
Employee(id: 102, name: "Erik Smith", salary: 500)

As you can see, “consider there is a lot to be calculated here…” has been printed, without decrementing the salary (Erik salary is less that 1000).

  • In case of declaring the decrement operator as static func —! (lhs: inout Employee, rhs: @autoclosure ()-> UInt) :
var erik = Employee(id: 102, name: "Erik Smith", salary: 500)
erik -! getAmountToDecrmement()
print(erik)
// Employee(id: 102, name: "Erik Smith", salary: 500)

It would log:

Employee(id: 102, name: "Erik Smith", salary: 500)

Without the “consider there is a lot to be calculated here…”.

Unlike the first case, since rhs is a closure, its evaluation delayed until the argument has needed to be used.

Wrapping up:

Using autoclosures makes our code to be more natural; As pretty simple example, the Swift logical AND operator (&&) declared as:

func &&<T : BooleanType>(lhs: T, rhs: @autoclosure () -> Bool) -> Bool

since if the lhs there is no need to evaluate the rhs (the result is false). We -as developers- are using it gracefully without even caring about whether if it deals with closures or not, that’s the obvious when using such a method.

Don’t hesitate to use autoclosures in the right place, they more simpler than being scary 👻.

Reference:

The Swift Programming Language — Closures: Autoclosures.

Author ✍️

Appreciate your feedback 👏 Stay tuned for more “Swift, more elegant code” topics.

Thanks for Reading!

--

--

Ahmad Fayyas

Software Engineer. If I’m not in front of my pc coding or playing video games, you can find me hanging out with family and friends, or sleeping!