Swift closure strange behaviour - ios

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

Related

Cannot assign value of type '(_) -> ()' to type '((String, String, String, Int) -> ())?'

I have a closure defined like this,
public var onLogCompletion:((_ printLog:String,_ fileName:String,_ functionName:String,_ lineNumber:Int) -> ())? = nil
Which is updated like this,
fileprivate func printerCompletion(printLog:String, fileName:String, functionName: String, lineNumber:Int) -> Void {
if onLogCompletion != nil {
onLogCompletion!(printLog, getFileName(name: fileName), functionName, lineNumber)
}
}
And using it like this,
Printer.log.onLogCompletion = { (log) in
//print(log)
//print(log.0)
}
Error:
Cannot assign value of type '(_) -> ()' to type '((String, String, String, Int) -> ())?'
But this is giving me above error and not sure what to do?
The same is working fine with Swift 3.x.
The reason its not working in Swift 4 is because of Distinguish between single-tuple and multiple-argument function types(SE-0110).
If you still want to work in a way you are doing in Swift 3 than you need to set the function type's argument list to enclosed with Double parentheses like this.
public var onLogCompletion:(((String,String,String,Int)) -> ())? = nil
Now you all set to go
Printer.log.onLogCompletion = { (log) in
//print(log)
//print(log.0)
}

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

nil-coalescing operator causes compiler to report ambiguity

I'd like to understand why the expanded method works while the nil-coalescing operator version tells that there is some ambiguity trying to compile it.
typealias SendBlock = ([NSArray]) -> Void
var callbacks = [SendBlock]()
func functionWithArgs(callback: SendBlock?) {
callbacks.append(callback ?? { _ in })
}
func functionWithArgsExpanded(callback: SendBlock?) {
if let cb = callback {
callbacks.append(cb)
} else {
callbacks.append({ _ in })
}
}
It’s because of the #autoclosure used on the rhs argument of ??. Something about that means it doesn’t like actually receiving a closure as the type of the thing being coalesced.
Just to demonstrate the location of the issue (don’t do this as a workaround), if you redefine a version of ?? without it, it’s fine:
typealias SendBlock = () -> Void
var callbacks = [SendBlock]()
infix operator !! { }
func !!<T>(optional: T?, defaultValue: T)->T {
return optional != nil ? optional! : defaultValue
}
func functionWithArgs(callback: SendBlock?) {
callbacks.append( callback !! { _ in })
}
functionWithArgs { _ in println("blah") }
// just to prove it works… prints “blah”
for x in callbacks { x() }
But if you amend the operator to use an #autoclosure:
func !!<T>(optional: T?, #autoclosure defaultValue: ()->T)->T {
return optional != nil ? optional! : defaultValue()
}
You get your error:
error: type of expression is ambiguous without more context
callbacks.append( callback !! { _ in })
^
I’d say this is a bug and you should file a report. In the mean-time your workaround is probably the best you’ll do.
I think it's just a bug in the compiler. It works just fine if you substitute String every place you have SendBlock.
Evidently, the compiler chokes on the use of the nil-coalescing operator with an Optional wrapping a function type. You get a more revealing error if you write it like this:
func functionWithArgs(callback: SendBlock?) {
let sb : SendBlock = { _ in }
callbacks.append(callback ?? sb)
}
The error reads: "Binary operator ?? cannot be applied to operands of type SendBlock? and SendBlock." To which one wants to reply: Of course it can! Those are exactly the sorts of operand to which it can be applied: an Optional and its unwrapped form.

How to use Swift #autoclosure

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)))
}

Resources