How to use Swift #autoclosure - closures
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
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.
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.
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.
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()
}
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
#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)))
}
Related
how to call a closure with argument in a function
func Test(clo: #escaping (String)->Void) { clo($0) } it return an error that says : anonymous closure argument not contained in a closure I want to be able to do: Test{ n in var name = n name = "Taha" print(name)} instead of hard coded String func Test(clo: #escaping (String)->Void) { clo("Taha") }
That is not how closures work in Swift. And the code you wrote doesn't even make sense. Why do you assign n to name then change the value of name just to print it ? The function with the closure will call the closure with a given value. You can think of closures as lambda functions. Here is an example: func test(_ closure: #escaping (String) -> Void) { closure("My string") } test { str in print(str) //prints " My string" } For what you are trying to achieve, just use a String parameter in your function: func test(_ str: String) { print(str) } test("My string") // prints "My string" Or use a closure that takes Void: func test(_ closure: #escaping (Void) -> Void) { closure() } test { print("My string") // prints "My string" }
Understanding a basic Swift compiler error
I understand why this produces a compiler error: let initialProducer = SignalProducer<Int, NoError>(value:42) let sideEffectProducer = initialProducer.on(next: { (answer: Int) in return _ }) The error is Cannot convert value of type '(Int) -> _' to expected argument type '(Int -> ())?' So the next parameter takes a closure with an Int parameter that returns Void whilst we're returning _ But why does this compile fine: let initialProducer = SignalProducer<Int, NoError>(value:42) let sideEffectProducer = initialProducer.on(next: { (answer: Int) in return "" }) we're returning a String, not Void so why does the compiler not complain?
_ isn't nothing. It is a pattern, or a part of a pattern that can match anything. It can also be used in an assignment statement to show that you don't care about the result. _ = foo() // Ignore result returned from foo In your closure, if you want to return nothing, then either: return or omit the return altogether if you're at the end of the closure. If you return _, Swift cannot figure out the signature of your closure. You can demonstrate that by doing: let bar = { return _ } // Unable to infer closure return type in current context If you remove the _, it compiles fine since bar becomes a () -> (). Swift could have given you a better error message like it does if you try to return _ from a function: func foo() { return _ // '_' can only appear in a pattern or on the left side of an assignment } So, why does return "" work? Here's a clue. There are some apparent oddness around single-line closures. Consider the following example which is similar to yours: func doit(handler: (Int) -> ()) { handler(17) print("doit is done") } doit() { (answer: Int) in //print(answer + 1) return "" } Running this produces the output: doit is done So, like your example doit is expecting a (Int) -> () closure, but we're passing a (Int) -> String closure. And it works... But, if you un-comment the print(answer + 1) line, the return "" then results in the error: Unexpected non-void return in void function
Use logical operator as combine closure in reduce
I am trying to reduce an array of Bools by applying the logical operator OR (||) using the following code, however I get an error: func reduceBools(values: [Bool]) -> Bool { return values.reduce(false, combine: ||) } Ambiguous reference to member '||' Analogously for integers the code works like a charm. func reduceInts(values: [Int]) -> Int { return values.reduce(0, combine: +) } I was able to make it work by adding a || function (code below) or using a { $0 || $1 } closure but I dislike these approaches and I would prefer simply passing the operator. func ||(lhs: Bool, rhs: Bool) -> Bool { return lhs || rhs } The same thing happens for the logical AND (&&) operator. How can I make it work without using the hack above?
As an alternative, you could use the following approach // || func reduceBoolsOr(values: [Bool]) -> Bool { return values.contains(true) } // && func reduceBoolsAnd(values: [Bool]) -> Bool { return !values.contains(false) } Note that .reduce comes with an overhead. If the end result is the importance of your question (rather than enquiring above the unexpected behaviour of || and && operators in this context), then perhaps the pragmatic approach above can be of help, even if it doesn't really reduce the array, however producing the same result due to the simple nature of the boolean type.
Swift 4.2+ / Xcode 10.0+ In modern versions of Swift there is allSatisfy function, which checks all elements for satisfying some rule. In OP's case: values.allSatisfy { $0 } UPD: To make this work for OR, do !values.allSatisfy{!$0} Thanks to Andy Weinstein
Following approach will work values.reduce(false) { $0 || $1 }
Ambiguous reference to member '||' means, that there are more than one possible candidates, from which compiler is not able to choose. In your case those are public func ||<T : BooleanType, U : BooleanType>(lhs: T, #autoclosure rhs: () throws -> U) rethrows -> Bool and public func ||<T : BooleanType>(lhs: T, #autoclosure rhs: () throws -> Bool) rethrows -> Bool probably your 'hack' using a { $0 || $1 } is the best solutions here.
This happens because of Swifts closure semantics. It takes your arguments and applies function to them, omitting argument names. protocol Numeric { ... public static func +(lhs: Self, rhs: Self) -> Self ... } In example with Ints, you would pass (Int, Int) into a closure, and + function in Numeric protocol expects exactly two ints to sum them. Thats why code like below works just fine [1, 2, 3, 4].reduce(0, +) Because you just took 2 ints, and applied function, which takes just two ints. If you write your own function, which would take just two argument, it would work as well. func myOwnAwesomeFunc<T: Numeric>(a: T, b: T) -> T { in return 1 // production ready } [1, 2, 3, 4].reduce(0, myOwnAwesomeFunc) // prints 1 Good so far. But why can't we write [true, false, true].reduce(false, ||) // yields Cannot invoke 'reduce' // with an argument list of type // '(Bool, (Bool, #autoclosure () throws -> Bool) throws -> Bool)' That's because this operator takes bool and a closure, which returns bool. Not bool, closure! But if it is like this, why aren't we writing true || { false }() ? Thats because of #autoclosure, which takes care of curly braces for us. Main question, why is it implemented this way, so we can't use Swifts awesome short-hand closure syntax with booleans? Idk
Here's another approach, I modified the reduceBools function to take the operator as a parameter - typealias LogicalOperator = ((Bool, #autoclosure () throws -> Bool) throws -> Bool) func reduceBools(values: [Bool], combine: LogicalOperator) -> Bool { var started: Bool = false return values.reduce(into: true, { (result, value) in result = started ? try! combine(result, value) : value // obviously up to you how you'd handle the try/catch started = true }) } let bools = [true, false, false, true] let result1 = self.reduceBools(values: bools, combine: ||) print(result1) // prints true let result2 = self.reduceBools(values: bools, combine: &&) print(result2) // prints false Or it could be more useful as an extension of Sequence - extension Sequence where Element == Bool { func reduce(_ combine: LogicalOperator) -> Bool { var started: Bool = false return self.reduce(into: true, { (result, value) in result = started ? try! combine(result, value) : value started = true }) } } print(bools.reduce(||)) // prints true
How to add #noescape annotation to optional closure
My function has this signature: func foo(bar: String, baz: ((String) -> ())? = nil) And now I want to make unecessary to escape self inside the given closure. But when I try this: func foo(bar: String, #noescape baz: ((String) -> ())? = nil) The compiler complains: #noescape may only be applied to parameters of function type Is it possible to use it in optional parameters?
Requirements If your requirements are the following: the baz param is a closure the baz param is marked with #noescape (because you want to omit self in the closure code) the baz param can be omitted during the invocation of foo Solution Then you can use the following syntax func foo(bar: String, #noescape baz: ((String) -> ()) = { _ in } ) { } As you can see the main difference from your code is that: here baz is not an optional type (but it's an "optional parameter") and its default value is an empty closure not a nil value. Examples As you requested you can now pass a closure to baz without the need of using self class Boo { let world = "world" func boo() { foo("hello") { (something) -> () in print(world) } } } And you can also omit the baz param class Boo { let world = "world" func boo() { foo("hello") } } Update: using a closure with return type different from Void In a comment below users TadeasKriz asked about how to use this approach with a closure having the return value different the Void. Here it is the solution func foo(bar: String, #noescape baz: ((String) -> (Int)) = { _ in return 0 } ) { } Here the baz param does required a closure with 1 param of type String and a return value of type Int. As you can see I added a default value to the param, a closure that does return 0. Please note that the default closure will never be used so you can replace 0 with any Int value you want. Now you can decide whether to use pass your closure to the baz param class Boo { let world = "world" func boo() { foo("hello") { (something) -> Int in print(world) return 100 } } } Or, again, you can totally omit the baz param. class Boo { let world = "world" func boo() { foo("hello") } }
Swift closure strange behaviour
Why does this code compile (in Playground): func text (f: String -> Void) { f("text") } func foo(s: String) { countElements(s) } text() { s in foo(s) } And this one doesn't: func text (f: String -> Void) { f("text") } func foo(s: String) { countElements(s) } text() { s in countElements(s) } With error message: Cannot convert the expression's type '(($T3) -> ($T3) -> $T2) -> (($T3) -> $T2) -> $T2' to type '_BuiltinIntegerLiteralConvertible' I can tell that there is something with return type, kinda Swift thinks that I want to return int but I just want to print it
The second version of the code works if you add an explicit return statement: text() { s in countElements(s) return } The reason for that to happens is that it uses implicit return, being a single statement closure, so it tries uses the return value of countElements, which doesn't match with the expected return type, Void. The explicit return fixes that. As to why it behaves in a different way, in the former case foo implicitly returns Void, which matches the closure return type. More info: Implicit Returns from Single-Expression Closures