Software Stewardship
Clashing Protocol Functions Cannot Be Disambiguated

I’m going to classify this as Swift issue but only because it’s a new language. The same problem exists in Objective-C. I’ve already reported it to Apple and it’s on Open Radar as well.

Description

Consider a class implementing two protocols with clashing function declarations (name + all parameter types). Currently there is no mechanism to disambiguate which function is being a) implemented and b) invoked. The compiler collapses the implementation of the clashing function to be invoked for both protocols even though these may have very different semantics (as function name will depend on the protocol context)

Regarding implementation, I propose that it’s disambiguated with extensions. Allow the following:

protocol Protocol1 {
    func doSomething()
}

protocol Protocol2 {
    func doSomething()
}

//  Compiler error - don't know which one is being implemented, use extension for this.
class Class1 : Protocol1, Protocol2 {
    func doSomething() {
        println("which one is it?");
    }
}

//  Fine - the compiler knows that it's Protocol1 that's being implemented *and* it's obvious to the programmer as well.
class Class2 : Protocol1 {
    func doSomething() {
        println("it's protocol1");
    }
}

//  Fine - again it's perfectly clear to both compiler and programmer what's going on.
extension Class2 : Protocol2 {
    func doSomething() {
        println("it's protocol2");
    }
}

Regarding invocation the compiler knows that there is an ambigous call and it with explicit casting it’s easy for the programmer to manually disambiguate:

var obj = Class2()
obj.doSomething()   //  Compiler error - it's not clear which function should be invoked, cast to explicit var var p1 = obj as Protocol1;
p1.doSomething()    //  Fine - invoke the Protocol1 implementation
var p2 = obj as Protocol2;
p2.doSomething()    //  Fine - invoke the Protocol2 implementation

This even works when extending classes to which we don’t have source code:

//  In library source code (we don't have it)
protocol LibraryProtocol1 {
    func doSomething()
}

class LibraryClass : LibraryProtocol {
    func doSomething() {
        //  whatever it may be
    }

    func doSomethingConditionally() {
        if(someCondition) {
            doSomething()
        }
    }
}

protocol LibraryProtocol2 {
    func doSomething()
}

//  Our source code
extension LibraryClass : LibraryProtocol2 {
    func doSomething() {
        //  something else to be done
    }
}

var obj = LibraryClass()
obj.doSomethingConditionally()    //  Invokes LibraryProtocol1.doSomething() because when LibraryClass module was compiled there was no ambiguity
obj.doSomething()   //  Error - this now has to be disambiguated manually

P.S.

I didn’t mention it in the bug report but there are two obvious workarounds to this problem:

  1. By convention prefix all your protocol names with protocol name:
protocol Protocol1 {
	func Protocol1_doSomething()
}

protocol Protocol2 {
	func Protocol2_doSomething()
}

I find it hard to believe that this would be widely followed and even if it is, it’s just a convention. And it’s pretty ugly.

  1. Build bridge classes:
protocol Protocol1 {
	func doSomething()
}

protocol Protocol2 {
	func doSomething()
}

class BridgeClassProtocol1 : Protocol1 {
	func doSomething() {
    	//	Invoke a specialized function for Protocol1 
        //	semantics on the Class object that we hold.
    }
}

class BridgeClassProtocol2 : Protocol2 {
	func doSomething() {
    	//	Invoke a specialized function for Protocol2 
        //	semantics on the Class object that we hold.
    }
}

This can work and what anyone that needs to work with clashing protocols that cannot be modified will do. But again it’s ugly and compiler should be able to help us out.


Last modified on 2014-06-24