[swift-evolution] Proposal: Universal dynamic dispatch for method calls

Gwendal Roué gwendal.roue at gmail.com
Tue Dec 8 22:21:52 CST 2015


> Le 9 déc. 2015 à 00:26, Kevin Ballard via swift-evolution <swift-evolution at swift.org> a écrit :
> 
>> Keep in mind that what I’m proposing here doesn’t actually change Swift’s runtime behavior at all. Note #2—you’re *required* to make protocol extension members `final` unless they’re listed in the protocol declaration. What I’m proposing is merely a series of compile-time statements used to tell swiftc that, yes, I know this is going to happen.

Whatever the direction this proposal is aiming at, please remember that it is desirable for some APIs to let adopting types "override" the default implementation provided by protocols.

The current static dispatch of protocol extensions allows adopting types to perform such an "override":

    protocol Feature { }
    extension Feature {
        func run() {
            // default implementation
        }
    }
    struct S : Feature {
        func run() {
            // "inherited" from Feature:
            (self as Feature).run()
            // extra code
            ...
        }
    }

This is important, because *without* this static dispatch, the protocol P *must* be turned into a *base class* as soon as three conditions are met:

- the implementation inside the run() function is rather complex
- it is desirable to let adopting types able to alter the default behavior.
- bloating the API and the documentation with extra global functions or extra methods that work around this limitation of the language is not desirable

    // Terse API with a Feature base class:
    class Feature {
        func run() {
            // rather complex implementation
        }
    }
    class C : Feature {
        func run() {
            // Inherited from Feature:
            super.ext()
            // extra code
            ...
        }
    }
    
    // Non-terse API with extra methods and documentation:
    protocol Feature { }
    extension Feature {
        func run() {
            self._run()
        }
        /// Don't call this method directly, but use run() instead.
        /// Adopting types may call this method in their own implementation
        /// of run().
        func _run() {
            // default implementation
        }
    }
    struct S : Feature {
        func run() {
            // "inherited" from Feature:
            self._run()
            // extra code
            ...
        }
    }

You have understood that I'm against those extra methods and documentation, and that I prefer the base class solution, despite protocols being all the rage.

Actually, the use case I'm talking here is not properly addressed by the current state of Swift because of a bug with mutating methods https://bugs.swift.org/browse/SR-142 which has the consequence of forcing APIs to keep on exposing a base class instead of a protocol (see https://github.com/groue/GRDB.swift/issues/12 for a practical problem).

Anyway, please remember that it is desirable for some APIs to let adopting types "override" the default implementation provided by protocols.

Gwendal Roué



More information about the swift-evolution mailing list