func test(f: () -> ()) {
let a = f
let b: () -> () = f // error
a()
b()
}
The only difference between a and b is whether the type is specified or not.
But what is the reason for the error to be printed only in b?
Please explain the clear difference between a and b.
The error is shown because 'b' and 'f' do have different signatures.
'b' is an escaping closure 'f' is not.
when doing:
let a = f
you are copying 'f'. The signature stays the same "nonescaping () -> ()".
Declaring a closure in function definition gives you implicit 'nonescaping' in body gives you implicit 'escaping'.
Decorating your closure in the header with #escaping will solve the problem.
func test(f: #escaping () -> ()) {
If you want to know more:
https://medium.com/swiftcommmunity/what-do-mean-escaping-and-nonescaping-closures-in-swift-d404d721f39d
Related
Given this code:
struct Foo {
func f() {}
}
let f = Foo.f // (Foo) -> () -> ()
Why does f have the type (Foo) -> () -> () and not (Foo) -> ()? Wouldn’t it make sense for instance methods like Foo.f to be directly interchangeable with free functions of type (Foo) -> …?
Why does f have the type (Foo) -> () -> () and not (Foo) -> ()?
That's just currently how unapplied instance method references are implemented; they're curried functions that follow the model of "give me an instance, and I'll give you back a partially-applied instance method" (partially applied with that instance).
However this is problematic in some areas, firstly because it's generally more useful for them to be of the form (Self, Args...) -> Ret, but also more importantly because it because it leads to issues around mutating methods. These end up looking like (inout Self) -> (Args...) -> Ret with the current system, which is problematic because the window of mutation for inout only lasts for the duration of the call.
This means the following currently compiles, but is actually undefined behaviour:
struct S {
var i: Int
mutating func increment() {
i += 1
}
}
var s = S(i: 0)
let unappliedIncrement = S.increment
let increment = unappliedIncrement(&s)
increment() // undefined behaviour
These are the main motivations behind SE-0042, which will change unapplied instance method references from being of the form (Self) -> (Args...) -> Ret to being of the form (Self, Args...) -> Ret (and with inout, Self will be inout Self – allowing the mutation of the instance without UB).
This proposal is yet to be implemented, but once it is, assuming empty parameter lists get flattened out (so you don't end up with a trailing Void parameter), Foo.f will indeed be of type (Foo) -> Void.
It's because, by saying this:
let f = Foo.f
you've described f as a class method, Foo.f. But in your declaration of Foo, f is an instance method. You have thus accidentally discovered the deep dark secret that instance methods are class methods in disguise; you are seeing the signature of the secret class method.
If you had said
let f = Foo().f
you would have gotten the expected result.
Here is my code:
protocol SomeProtocol {
}
class A: SomeProtocol {
}
func f1<T: SomeProtocol>(ofType: T.Type, listener: (T?) -> Void) {
}
func f2<T: SomeProtocol>(ofType: T.Type, listener: ([T]?) -> Void) {
}
func g() {
let l1: (SomeProtocol?) -> Void = ...
let l2: ([SomeProtocol]?) -> Void = ...
f1(ofType: A.self, listener: l1) // NO ERROR
f2(ofType: A.self, listener: l2) // COMPILE ERROR: Cannot convert value of type '([SomeProtocol]?) -> Void' to expected argument type '([_]?) -> Void'
}
What is the problem with the second closure having an argument of an array of generic type objects?
Swift 4.1 Update
This is a bug that was fixed in this pull request, which will make it into the release of Swift 4.1. Your code now compiles as expected in a 4.1 snapshot.
Pre Swift 4.1
This just looks like you're just stretching the compiler too far.
It can deal with conversions from arrays of sub-typed elements to arrays of super-typed elements, e.g [A] to [SomeProtocol] – this is covariance. It's worth noting that arrays have always been an edge case here, as arbitrary generics are invariant. Certain collections, such as Array, just get special treatment from the compiler allowing for covariance.
It can deal with conversions of functions with super-typed parameters to functions with sub-typed parameters, e.g (SomeProtocol) -> Void to (A) -> Void – this is contravariance.
However it appears that it currently cannot do both in one go (but really it should be able to; feel free to file a bug).
For what it's worth, this has nothing to do with generics, the following reproduces the same behaviour:
protocol SomeProtocol {}
class A : SomeProtocol {}
func f1(listener: (A) -> Void) {}
func f2(listener: ([A]) -> Void) {}
func f3(listener: () -> [SomeProtocol]) {}
func g() {
let l1: (SomeProtocol) -> Void = { _ in }
f1(listener: l1) // NO ERROR
let l2: ([SomeProtocol]) -> Void = { _ in }
f2(listener: l2)
// COMPILER ERROR: Cannot convert value of type '([SomeProtocol]) -> Void' to
// expected argument type '([A]) -> Void'
// it's the same story for function return types
let l3: () -> [A] = { [] }
f3(listener: l3)
// COMPILER ERROR: Cannot convert value of type '() -> [A]' to
// expected argument type '() -> [SomeProtocol]'
}
Until fixed, one solution in this case is to simply use a closure expression to act as a trampoline between the two function types:
// converting a ([SomeProtocol]) -> Void to a ([A]) -> Void.
// compiler infers closure expression to be of type ([A]) -> Void, and in the
// implementation, $0 gets implicitly converted from [A] to [SomeProtocol].
f2(listener: { l2($0) })
// converting a () -> [A] to a () -> [SomeProtocol].
// compiler infers closure expression to be of type () -> [SomeProtocol], and in the
// implementation, the result of l3 gets implicitly converted from [A] to [SomeProtocol]
f3(listener: { l3() })
And, applied to your code:
f2(ofType: A.self, listener: { l2($0) })
This works because the compiler infers the closure expression to be of type ([T]?) -> Void, which can be passed to f2. In the implementation of the closure, the compiler then performs an implicit conversion of $0 from [T]? to [SomeProtocol]?.
And, as Dominik is hinting at, this trampoline could also be done as an additional overload of f2:
func f2<T : SomeProtocol>(ofType type: T.Type, listener: ([SomeProtocol]?) -> Void) {
// pass a closure expression of type ([T]?) -> Void to the original f2, we then
// deal with the conversion from [T]? to [SomeProtocol]? in the closure.
// (and by "we", I mean the compiler, implicitly)
f2(ofType: type, listener: { (arr: [T]?) in listener(arr) })
}
Allowing you to once again call it as f2(ofType: A.self, listener: l2).
The listener closure in func f2<T: SomeProtocol>(ofType: T.Type, listener: ([T]?) -> Void) {...} requires its argument to be an array of T, where T is a type that implements SomeProtocol. By writing <T: SomeProtocol>, you are enforcing that all elements of that array are of the same type.
Say for example you have two classes: A and B. Both are completely distinct. Yet both implement SomeProtocol. In this case, the input array cannot be [A(), B()] because of the type constraint. The input array can either be [A(), A()] or [B(), B()].
But, when you define l2 as let l2: ([SomeProtocol]?) -> Void = ..., you allow the closure to accept an argument such as [A(), B()]. Hence this closure, and the closure you define in f2 are incompatible and the compiler cannot convert between the two.
Unfortunately, you cannot add type enforcement to a variable such as l2 as stated here. What you can do is if you know that l2 is going to work on arrays of class A, you could redefine it as follows:
let l2: ([A]?) -> Void = { ... }
Let me try and explain this with a simpler example. Let's say you write a generic function to find the greatest element in an array of comparables:
func greatest<T: Comparable>(array: [T]) -> T {
// return greatest element in the array
}
Now if you try calling that function like so:
let comparables: [Comparable] = [1, "hello"]
print(greatest(array: comparables))
The compiler will complain since there is no way to compare an Int and a String. What you must instead do is follows:
let comparables: [Int] = [1, 5, 2]
print(greatest(array: comparables))
Have nothing on Hamish's answer, he is 100% right. But if you wanna super simple solution without any explanation or code just work, when working with array of generics protocol, use this:
func f1<T: SomeProtocol>(ofType: T.Type, listener: (T?) -> Void) {
}
func f2<Z: SomeProtocol>(ofType: Z.Type, listener: ([SomeProtocol]?) -> Void) {
}
I've the following generic function (not part of any class):
func execFuncWithGenericParameter<T, U>(f: (T) -> U){
print(f("Hello World"))
}
I'd like to call this function in with a closure like this:
execFuncWithGenericParameter(f: { (p: String) -> Int in
print(p)
return 4711
})
But the compiler (iPad Swift Playground) tells me that "(String) -> U is not convertible to (T) -> U".
Naturelly I've done done some investigation and was the opinion that the Compiler automatically will infer the types.
Thanks.
The types are being inferred as far as the nature of f: (T) -> U is concerned. The problem is the call to f inside your first method.
Ask yourself: what if T were not String? Then f("Hello world") would be illegal. That is the quandary you've left the compiler with — and it rightly refuses to deal with it.
Here's a legal version:
func execFuncWithGenericParameter<T,U>(f: ((T) -> U), param:T){
f(param)
}
execFuncWithGenericParameter(f: { (p: String) -> Int in
print(p)
return 4711
}, param:"Hello")
Now the first method knows that param will be a T, so all is well. And in the second method, when we call the first method, T is String and param is a String, so it compiles (and runs, and prints).
Precedence rules for same name and signature function & closure
When defining a closure and a function with same name (say, foo) and signature, it seems as if the closure takes precedence when calling said (seemingly ambiguous) foo.
// Int -> () function
func foo(num: Int) { print("function \(num)")}
// Int -> () closure
let foo: (Int) -> () = { print("closure \($0)")}
/* or...
let foo = { (num: Int) in print("closure \(num)")} */
foo(1) // closure 1
If I option-click the two declarations, they point at each other under the label Related declarations, differing in how they are referred to as:
Declaration: func foo(num: Int)
Related Declarations: foo
...
Declaration: let foo: (Int) -> ()
Related Declarations: foo(_:)
If we attempt to define the same double-foo definitions for a zero-argument function & closure, we get a compile time error prompting invalid redeclaration
// () -> () function
func foo() { print("function")}
// () -> () closure
let foo: () -> () = { print("closure")}
/* or...
let foo = { () in print("closure")} */
/* error: invalid redeclaration of 'foo' */
Question: Why is it that the first case above is considered non-ambiguous, and why does the closure take precedence over the function when calling foo(..) (i.e., in the overload resolution of foo(...)?
I haven't been able to find any official docs (or existing SO thread) explaining this.
Tested for Swift 2.2/Xcode 7.3 and Swift 3.0-dev/IBM Sandbox (with function signature modified to func foo(_ num: Int) { ... }).
In Swift, you cannot name a variable and a function or closure the same thing if no parameters are passed. Although we call them in different ways (foo for a variable and foo() for a function. we will get an invalid redeclaration of foo. The compiler treats the variable as if it has no parameters like the function does. Consider some of these cases:
class X {
var value : Int = 0
func value() -> Int { return 1 } // Invalid redeclaration of 'value()'
}
let x = X()
What is x.value in this case? Is it Int or is it () -> Int? It's legal and useful to treat the methods of classes as if they were closures.
What if we're even more tricky, and do this:
class X {
let value: () -> Int = { 2 }
func value() -> Int { return 1 } // Invalid redeclaration of 'value()'
}
let x = X()
let v = x.value() // ????
Should Swift use the property value and then call it? Or should it call the method value()? Closures are completely legal as properties.
However, using parameters, you can name a function and a variable/Closure the same thing, although I would advise you not to. In this case you should try to name the variable and the function something that describes what they are and/or what they do to make your code more readable by others. I would suggest naming the variable something like
class X {
// Int -> () function
func foo(number num: Int) { print("function \(num)")}
// Int -> () closure
let foo: (Int) -> () = { print("closure \($0)")}
}
because by naming the function it will allow user to know exactly which function your are calling when your method and variable names are same. user can know what parameter is for they are passing. by naming when you call the methods like below.
let x = X()
x.foo(number: 2)
x.foo(3)
and you CMD + click it will point you to the exact method or variable/closure you have written for.
consider previous example:
class X {
// Int -> () function
func foo(number num: Int) { print("function \(num)")}
// Int -> () closure
let foo: (Int) -> () = { print("closure \($0)")}
}
let x = X()
x.foo(2) // x.foo(num: Int)
x.foo(3) // x.foo
Despite you called the correct method or closure. by cmd + click it will point you to the closure only.
In order to avoid code repetition I'm trying to find a way to infer argument type Tor () -> T from a variable of type U.
I don't know if I'm clear enough so this is what I wan't to do:
func f<T>(closure: () -> T) -> String {
return "closure: () -> \(T.self)"
}
func f<T>(value: T) -> String {
return "value: \(T.self)"
}
f("test1") // -> "value: Swift.String"
f({ return "test2" }) // -> "closure: () -> Swift.String"
func foo<U>(bar: U) -> (String, String) {
return ("\(U.self)", f(bar))
}
foo("test3") // (.0 "Swift.String", .1 "value: Swift.String")
foo({ return "test4" }) // (.0 "() -> Swift.String", .1 "value: () -> Swift.String")
I expected foo({ return "test4" }) to call f<T>(closure: () -> T) function instead of f<T>(value: T). Why Swift can't infer that bar: U match with () -> T pattern ?
TL;DR: Type data is lost with foo() / U.
I suspect that you are coming from a C++ background.
This would work in C++ as C++ infers the type based on the call site. Swift is different though, in that the generic parameter is the only source of type data. In your case, you are providing no information about the type U, so the compiler does not know how it could convert it to the closure type. Your value override of f() works because you are not placing any conditions on T, whereas in your closure type override, you are specifying a specific form (a closure). Since all type data is lost by your call through foo that takes generic (think blank) type U, the compiler does not have enough information, and cannot deduce a conversion from (blank) to a closure.
This is the topic of "type reification", and there is a lengthy discussion on this on devforums.apple.com.