[swift-evolution] Pitch: Partial Implementations

Jonathan Hull jhull at gbis.com
Thu Mar 23 15:09:22 CDT 2017


-1

I would much rather see the ability to introduce stored properties in extensions (within the module) + simple file-based submodules.

Also, I think you are missing important parts of the extensions + fileprivate pattern.  Sure, a big part of it is being able to group conformances together in code.  But another large part is being able to group related code across classes and structs (avoiding the need for “friends”).

It would not, as you say, completely eliminate fileprivate from my code, it would just further complicate things by having to remember to use a different keyword (‘partial’) when I needed storage in extensions.

There have been a few proposals now, where I think the core idea was to enforce best practices from another language.  But these end up being harmful (despite good intentions) because swift is a different beast, and thus will have different best practices.  In particular, extensions allow cross-cutting concerns to be placed together.  I can have a little bit of this class and a little bit of that one. That leads to different opportunities and challenges than those found in languages where a class has to be defined in one place, and it means we need different solutions…

As you say, we do have an issue of very large files being created right now, but there isn’t a type/scope-based solution which will actually fix that, because they all block those cross-cutting concerns. I suspect this will stop being an issue once we have both submodules and stored properties in extensions (which have both been listed as likely enhancements, but out of scope for Swift 4).


> On Mar 23, 2017, at 11:12 AM, Charles Srstka via swift-evolution <swift-evolution at swift.org> wrote:
> 
> 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
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution



More information about the swift-evolution mailing list