154

I noticed when writing an assert in Swift that the first value is typed as

@autoclosure() -> Bool 

with an overloaded method to return a generic T value, to test existence via the LogicValue protocol.

However sticking strictly to the question at hand. It appears to want an @autoclosure that returns a Bool.

Writing an actual closure that takes no parameters and returns a Bool does not work, it wants me to call the closure to make it compile, like so:

assert({() -> Bool in return false}(), "No user has been set", file: __FILE__, line: __LINE__) 

However simply passing a Bool works:

assert(false, "No user has been set", file: __FILE__, line: __LINE__) 

So what is going on? What is @autoclosure?

Edit: @auto_closure was renamed @autoclosure

6 Answers 6

278

Consider a function that takes one argument, a simple closure that takes no argument:

func f(pred: () -> Bool) { if pred() { print("It's true") } } 

To call this function, we have to pass in a closure

f(pred: {2 > 1}) // "It's true" 

If we omit the braces, we are passing in an expression and that's an error:

f(pred: 2 > 1) // error: '>' produces 'Bool', not the expected contextual result type '() -> Bool' 

@autoclosure creates an automatic closure around the expression. So when the caller writes an expression like 2 > 1, it's automatically wrapped into a closure to become {2 > 1} before it is passed to f. So if we apply this to the function f:

func f(pred: @autoclosure () -> Bool) { if pred() { print("It's true") } } f(pred: 2 > 1) // It's true 

So it works with just an expression without the need to wrap it in a closure.

Sign up to request clarification or add additional context in comments.

8 Comments

Actually the last one, doesn't work. It should be f({2 >1}())
@JoelFischer I am seeing the same thing as @JackyBoy. Calling f(2 > 1) works. Calling f({2 > 1}) fails with error: function produces expected type 'Bool'; did you mean to call it with '()'?. I tested it in a playground and with the Swift REPL.
I somehow read the second to last answer as the last answer, I'll have to double check, but it would make sense if it failed, as you are basically putting a closure inside a closure, from what i understand.
there is a blog post about the reason they did that developer.apple.com/swift/blog/?id=4
Great explanation. Note also that in Swift 1.2 'autoclosure' is now an attribute of the parameter declaration, so it's func f(@autoclosure pred: () -> Bool)
|
31

Here's a practical example — my print override (this is Swift 3):

func print(_ item: @autoclosure () -> Any, separator: String = " ", terminator: String = "\n") { #if DEBUG Swift.print(item(), separator:separator, terminator: terminator) #endif } 

When you say print(myExpensiveFunction()), my print override overshadows Swift's print and is called. myExpensiveFunction() is thus wrapped in a closure and not evaluated. If we're in Release mode, it will never be evaluated, because item() won't be called. Thus we have a version of print that doesn't evaluate its arguments in Release mode.

1 Comment

I am late to the party, but what is the impact of evaluating myExpensiveFunction()?. If instead of using autoclosure you pass the function to print like print(myExpensiveFunction), what would be the impact? Thanks.
11

Description of auto_closure from the docs:

You can apply the auto_closure attribute to a function type that has a parameter type of () and that returns the type of an expression (see Type Attributes). An autoclosure function captures an implicit closure over the specified expression, instead of the expression itself. The following example uses the auto_closure attribute in defining a very simple assert function:

And here's the example apple uses along with it.

func simpleAssert(condition: @auto_closure () -> Bool, message: String) { if !condition() { println(message) } } let testNumber = 5 simpleAssert(testNumber % 2 == 0, "testNumber isn't an even number.") 

Basically what it means is you pass a boolean expression as that first argument instead of a closure and it automatically creates a closure out of it for you. That's why you can pass false into the method because it is a boolean expression, but can't pass a closure.

3 Comments

Note that you don't actually need to use @auto_closure here. The code works fine without it: func simpleAssert(condition: Bool, message: String) { if !condition { println(message) } }. Use @auto_closure when you need to evaluate an argument repeatedly (e.g., if you were implementing a while-like function) or you need to delay evaluation of an argument (e.g., if you were implementing short-circuiting &&).
@nathan Hi, nathan. Could you please cite me a sample regarding the use of autoclosure with a while-like function? I don't seem to figure that out. Thanks very much in advance.
@connor You might want to update your answer for Swift 3.
4

This shows a useful case of @autoclosure https://airspeedvelocity.net/2014/06/28/extending-the-swift-language-is-cool-but-be-careful/

Now, the conditional expression passed as the first parameter to until will be automatically wrapped up into a closure expression and can be called each time around the loop

func until<L: LogicValue>(pred: @auto_closure ()->L, block: ()->()) { while !pred() { block() } } // doSomething until condition becomes true until(condition) { doSomething() } 

Comments

3

It's just a way to get rid of the curly braces in a closure call, simple example:

 let nonAutoClosure = { (arg1: () -> Bool) -> Void in } let non = nonAutoClosure( { 2 > 1} ) let autoClosure = { (arg1: @autoclosure () -> Bool) -> Void in } var auto = autoClosure( 2 > 1 ) // notice curly braces omitted 

Comments

0

@autoclosure

@autoclosure converts(wraps) expression inside function parameter in a closure[About]

Pros:

  • easy to read assert(2 == 5, "failed")
  • curly braces are not used

Cons

  • hard to read. When you pass a function inside @autoclosure it is not clear that this function will be deferred(because it is closure inside). fooWithAutoClosure(a: foo0()) - foo0() will be called not immediately as we expect reading this line

Overusing autoclosures can make your code hard to understand. The context and function name should make it clear that evaluation is being deferred.

Official doc

  • @autoclosure doesn't take any parameters
    func foo(p: @autoclosure () -> Void) 
  • @autoclosure accept any function with only appropriate returned type

More examples

//functions block func foo0() -> String { return "foo0" } func foo1(i1: Int) -> String { return "foo1 " + String(i1) } func foo2(i1: Int, i2: Int) -> String { return "foo2 " + String(i1 + i2) } 
//closures block func fooWithClosure0(p: () -> String) -> String { return "fooWithClosure0 " + p() } func fooWithClosure1(p: (Int) -> String) -> String { return "fooWithClosure1 " + p(1) } func fooWithClosure2(p: (Int, Int) -> String) -> String { return "fooWithClosure2 " + p(1, 2) } 
//@autoclosure func fooWithAutoClosure(a: @autoclosure () -> String) -> String { return "fooWithAutoClosure " + a() } 
//test closures func testClosures() { XCTAssertEqual("fooWithClosure0 foo0", fooWithClosure0(p: foo0)) XCTAssertEqual("fooWithClosure1 foo1 1", fooWithClosure1(p: foo1)) XCTAssertEqual("fooWithClosure2 foo2 3", fooWithClosure2(p: foo2)) XCTAssertEqual("fooWithClosure2 foo2 3", fooWithClosure2(p: { (i1, i2) -> String in return "fooWithClosure2 " + "foo2 " + String(i1 + i2) })) } 
//test @autoclosure func testAutoClosures() { XCTAssertEqual("fooWithAutoClosure HelloWorld", fooWithAutoClosure(a: "HelloWorld")) //"HelloWorld" is String as returned value of @autoclosure XCTAssertEqual("fooWithAutoClosure foo0", fooWithAutoClosure(a: foo0())) XCTAssertEqual("fooWithAutoClosure foo1 1", fooWithAutoClosure(a: foo1(i1: 1))) XCTAssertEqual("fooWithAutoClosure foo2 3", fooWithAutoClosure(a: foo2(i1: 1, i2: 2))) } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.