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
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution