Reputation: 6597
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
Upvotes: 154
Views: 33116
Reputation: 34175
@autoclosure
@autoclosure
converts(wraps) expression inside function parameter in a closure
[About]
Pros:
assert(2 == 5, "failed")
Cons
@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 lineOverusing autoclosures can make your code hard to understand. The context and function name should make it clear that evaluation is being deferred.
@autoclosure
doesn't take any parameters
func foo(p: @autoclosure () -> Void)
@autoclosure
accept any function with only appropriate returned typeMore 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)))
}
Upvotes: 0
Reputation: 6255
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
Upvotes: 3
Reputation: 3421
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.
Upvotes: 278
Reputation: 534893
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.
Upvotes: 31
Reputation: 48055
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()
}
Upvotes: 4
Reputation: 64634
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.
Upvotes: 11