Swift: Disambiguating between methods as curry type functions (<Swift 2.2 only)

Note that this issue has been fixed with the release of Swift 2.2 - see the Explicit Member Expression section of the Swift Language book.


In swift mode, you can make an optional method call, which may or may not be implemented according to the protocol:

@objc protocol F {
    optional func f(#p1: String, p2: String) -> String
}

@objc class C: F {
    func f(#p1: String, p2: String) -> String { return "0 \(p1) \(p2)" }
}

let c: F = C()

c.f?(p1: "1", p2: "2") // --> "0 1 2"

      

Or, you can also bind the method to a local constant:

if let f = c.f {
    f(p1: "1", p2: "2")
}

      

This is an elegant way to wrap some code that should only happen if the method is implemented, not just the call itself. Note, however, that unlike respondsToSelector:

NSObjectProtocol

optional binding does not validate named parameters. So, if you have two methods that differ only in parameter names, you get a Ambiguous use of 'f'

compile-time error:

@objc protocol F {
    optional func f(#p1: String, p2: String) -> String
    optional func f(#x: String, y: String) -> String
}

@objc class C: F {
    func f(#p1: String, p2: String) -> String { return "0 \(p1) \(p2)" }
    func f(#x: String, y: String) -> String { return "0 \(x) \(y)" }
}

let c: F = C()

c.f?(p1: "1", p2: "2") // --> "0 1 2"
c.f?(x: "1", y: "2") // --> "0 1 2"

if let f = c.f { // Ambiguous use of 'f'
    f(p1: "1", p2: "2")
}

      

So this is a Swift-Obj C compatibility issue. Or am I missing something and there is actually some Swift syntax that would remove the ambiguity between the two methods? Or should I move away from developing code that uses optional methods and just have multiple protocols to accommodate a variety of needs (which can in turn define cram types with more protocol names)?

Edit

My initial thinking was in line with the answer Sulthan

below. However, I am now starting to lean towards his original comment: "This seems to be an omission in grammar, I would report it." The reason for this has to do with the analog case of referenced instance methods as curry functions like:

class C {
    func f() -> String { return "I'm a C" }
}

let c = C()
c.f() //--> "I'm a C"

let f = C.f(c)
f() //--> "I'm a C"

      

This is good and people are already making good use of this core Swift feature.

Of course, you can do the same with named parameters:

class D {
    func g(#x: Int) -> String { return "Gave me \(x)" }
    func g(#y: Int) -> String { return "Give me \(y)" }
}

let d = D()
d.g(x: 5) //--> "Gave me 5"
d.g(y: 15) //--> "Gave me 15"

D.g(d)(x: 5) //--> "Gave me 5"

      

Only again, as with the additional methods above, I don't know how to eliminate the ambiguity between "f" and "g" without calling immediately:

let g = D.g(d) // Ambiguous use of 'g'
g(x: 5)

      

Note that the type annotation will help (assuming the types were declared differen, of course):

class D {
    func g(#x: Int) -> String { return "Gave me \(x)" }
    func g(#y: Double) -> String { return "Give me \(y)" }
}

let d = D()
let g: (x: Int) -> String = D.g(d)
g(x: 5) //--> "Gave me 5"

      

However, the compiler ignores the name of the parameter in (x: Int)

, and a link is still ambiguous if both x

and y

were Int

...

Similarly:

let p = (x: 0, y: 1)
p is (a: Int, b: Int) //--> true

      

though:

let q: (a: Int, b: Int) = (x: 0, y: 1) //--> Error: '(x: Int, y: Int)' is not convertible to '(a: Int, b: Int)`

      

+3


source share


1 answer


I just checked the relevant parts of the Swift grammar, so I'm pretty sure there is currently no way in the grammar to differentiate the two functions. Relevant parts

Expressions

Implicit-member-expression โ†’. identifier



It's just not possible to add external parameters for identifiers.

Note, however, that optional methods do not exist in pure swift, they only exist because Obj-C have them, so there is no big reason why Swift has a special grammar for optional methods. Also note that even Obj-C has no special grammar to test for the existence of optional methods. Obj-C uses reflection - the call respondsToSelector

will give you the information you need. And you can do the same in Swift. See What is the quick equivalent of ToSelector's answer?

+2


source







All Articles