[swift-evolution] Pitch: Partial Implementations
Charles Srstka
cocoadev at charlessoft.com
Thu Mar 23 13:12:39 CDT 2017
MOTIVATION:
In current Swift, a pattern has emerged among some developers, in order to logically group parts of a class or struct’s declaration, particularly around protocols:
class Foo {
…
}
extension Foo: SomeProtocol {
...
}
extension Foo: SomeOtherProtocol {
...
}
This has certain appealing characteristics; in addition to the obvious organizational property, this pattern also keeps protocol implementations close to the declaration of conformance to the protocol. Unfortunately, there are a couple of problems:
1. Extensions cannot contain stored properties. This means that if a protocol requires a property, and it makes sense for that property to be stored, its conformance cannot be completely contained within the extension, but rather some of it must be in the main declaration.
2. It’s not uncommon for these protocol conformances to need access to the type’s private internal state, but extensions do not have access to private members within the state. This necessitates declaring the needed state as fileprivate rather than private, a fact has been a primary rallying point in the battle that’s currently raging on this mailing list over whether we should keep the ‘private’ access modifier, and which I would surmise is probably the major remaining use of ‘fileprivate’ in modern Swift code.
3. Since members that are declared ‘fileprivate’ cannot be accessed outside the file, these protocol conformance extensions must belong to the same file as the original declaration, which can lead to very long file sizes when the code to implement a protocol is very long, or when a type supports a large number of protocols.
PROPOSED SOLUTION:
Add a keyword to declare only part of a type’s implementation. I am suggesting ‘partial’ as the keyword, but this can be changed for a better name if needed. Partial conformances would be declared like this:
class Foo {
private func somePrivateMethod() { … }
}
partial Foo: SomeProtocol {
var someRequiredProperty: Int = 5
func someRequiredMethod() {
self.somePrivateMethod()
}
}
partial Foo: SomeOtherProtocol {
func someOtherRequiredMethod() {
self.somePrivateMethod()
}
}
When compiling this, the compiler would simply treat all the contents of partial declarations as if they were located within the original declaration, making the above equivalent to this:
class Foo: SomeProtocol, SomeOtherProtocol {
private func somePrivateMethod() { … }
var someRequiredProperty: Int = 5
func someRequiredMethod() {
self.somePrivateMethod()
}
func someOtherRequiredMethod() {
self.somePrivateMethod()
}
}
Obviously, partial declarations would only be allowed within the same module (or submodule, once we get them) as the original declaration.
The advantages to this approach are:
1. Given a pattern that many developers are adopting, this proposal would provide a mechanism to follow that pattern properly instead of repurposing a mechanism—extensions—which was intended for something else. The Swift manual claims that extensions are meant to add things “to a type that is declared elsewhere, or even to a type that you imported from a library or a framework,” not for separating your own code into parts.
2. Partial implementations can now implement the entirety of a protocol, including stored properties if the protocol necessitates them.
3. Since the contents of all partial implementations are considered to be part of the same declaration, the contents of partial implementations can access private members, which should allow the almost complete elimination of ‘fileprivate’ from developers’ codebases, simplifying the access control model.
4. Since partial implementations are not dependent on file-based organization, they can be stored in separate files, as long as those files are compiled into the same module, thus allowing for smaller, leaner, source files that are easier to read and understand.
What do you think?
Charles
More information about the swift-evolution
mailing list