3

Please note that this issue has been resolved with the release of Swift 2.2 – see “Explicit Member Expression” section of The Swift Programming Language book.


In swift you can make an optional call to a method that that may or may not be implemented in conformance to a 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 optionally 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. Notice, however, that unlike the respondsToSelector: of the NSObjectProtocol, optional binding does not check for the named parameters. So, if you have two methods that only differ in the parameter names, you get 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")
}

This then is an issue of Swift - Obj C interoperability. Or have I missed something and there is in fact some Swift syntax available that would disambiguate between the two methods? Or should I quit designing code that makes use of optional methods and simply have multiple protocols to cater for diversity of needs (which may in turn cram type definitions with a lot of protocol names)?

Edit

My initial thinking was along the lines of Sulthan's answer below. However, I am now beginning to lean more towards his original comment: "This seems to be an omission in the grammar. I would report it." The reason for this has to do with the analogue case of referencing instance methods as curried functions of the type:

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 nice and people are already making good use of this fundamental feature of Swift.

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 optional methods above, I do not know how to disambiguate between 'f' and 'g' without the immediate call:

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

Note that type annotation would help (given the types were declared as 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 parameter name in (x: Int) and the reference would still be ambiguous if both x and y were an Int...

Similarly:

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

even though:

let q: (a: Int, b: Int) = (x: 0, y: 1) //--> Error: '(x: Int, y: Int)' is not convertible to '(a: Int, b: Int)`
Milos
  • 2,728
  • 22
  • 24
  • 1
    This seems to be an omission in the grammar. I would report it. – Sulthan Apr 17 '15 at 12:21
  • With regards to reporting it: This is part of a more fundamental issue of whether pure Swift protocols should have optional methods. Whether this is worth reporting depends on whether it is purely a meter of interoperability with Obj C, in which case introducing new syntax in order to better translate from Obj C may not be sufficiently warranted? – Milos Apr 17 '15 at 12:27
  • 1
    This is definitely an interesting question. Swift has included many features from functional languages but the naming of functions - which was enforced by the need for Obj-C interoperability - doesn't work well with it. Before reporting it, maybe raise the questions on apple dev forums. – Sulthan Apr 17 '15 at 13:32
  • Just as soon as I get a breather from work. Thanks Sulthan. I hope you don't mind me only +1 your answer as I would like to keep the question open for a while. Someone might have some ideas, eg. about disambiguation in the case of instance methods as curried functions of the type, etc... Speaking of which, I'm not happy with the question title, do you have any ideas on how it can be improved (whilst keeping it succinct)? – Milos Apr 17 '15 at 13:43
  • I believe that function currying uses the same grammar - it's an "implicit-member-expression" so I don't think there's a workaround. In this case it's a grammar omission. Optional methods are not really pure swift but currying is, so that's something that should be reported IMHO. – Sulthan Apr 17 '15 at 13:48
  • I'm not sure I understand how is 'implicit-member-expression' (i.e. something like `let b: NSColor = .blackColor()`) the relevant grammar spec. As I understand, that is syntactic sugar. In contrast, in the words of Chris Lattner: "It is an important (thought subtle) truth that methods are represented as curried functions in the type system. This is why unbound and partially applies methods 'just work' for a variety of things." – Milos Apr 17 '15 at 14:10
  • And: "check out the type signature of a mutating vs nonmutating struct method when partially applied: mutating methods simply bind their `self` argument as `inout`. **This is one of the keys of how the mutation model hangs together**." ([link](https://devforums.apple.com/message/1013710#1013710)) – Milos Apr 17 '15 at 14:13

1 Answers1

2

I have just checked the relevant parts of Swift grammar so I am certain there is currently no way in the grammar to distinguish between those two functions. The relevant parts are

Expressions

Implicit-member-expression → .­identifier

There is just no way to add extern parameters to identifiers.

However, note that optional methods do not exist in pure swift, they are there only because Obj-C have them, so there is no big reason for Swift to have special grammar for optional methods. Also note that even Obj-C doesn't have a special grammar for checking of existence of optional methods. Obj-C uses reflection - calling respondsToSelector will give you the required information. And you can do the same in Swift. See What is the swift equivalent of respondsToSelector?

Community
  • 1
  • 1
Sulthan
  • 128,090
  • 22
  • 218
  • 270
  • 1
    Thank you for you answer. I have initially been thinking the same way. Except that your initial comment got me thinking... Please see the updated question. – Milos Apr 17 '15 at 12:54