> On Feb 23, 2017, at 2:56 PM, Nevin Brackett-Rozinsky via swift-evolution 
> <[email protected]> wrote:


> 
> The prevailing view from recent discussions is that there should be just one 
> access level more fine-grained than ‘internal’, and it should be spelled 
> ‘private’. Let us leave aside for the moment what its exact meaning should 
> be, and consider the other end of the scale.

I think the submodule discussion now goes against this being a prevailing view 
- submodules are directly tied to be an access control level *above* 
private/fileprivate.

My expectation is that with submodules, “fileprivate” would go away.

> Moreover, object-oriented programming is just as much a first-class citizen 
> in Swift as protocol-oriented programming is, so we should treat it as such. 
> Classes are inherently inheritable: when one writes “class Foo {}”, then Foo 
> has a default visibility of ‘internal’, and by default it can have 
> subclasses. That is a straightforward model, and it is easy to work with.

There is nothing about OOP which requires subclass-based polymorphism. Rust and 
Go are two examples of modern languages that do not support subclassing at all.

In reality, there is less agreement on what “Object Oriented Programming” means 
to technologists than there is about the real meaning of “Moore’s Law"

> One of the reasons ‘public’ was previously chosen for closed classes is to 
> provide a “soft default” for library authors, so they can prevent subclassing 
> until they decide later whether to allow it in a future release. This is a 
> misguided decision, as it prioritizes the convenience of library authors over 
> the productivity of application developers. Library authors have a 
> responsibility to decide what interfaces they present, and we should not 
> encourage them to release libraries without making those decisions.

I think SE-0117 and the discussion behind it is pretty clear about why this is 
the default. It is not about convenience, but safety.
> 
> Moreover, we need to trust client programmers to make reasonable choices. If 
> a library mistakenly allows subclassing when it shouldn’t, all a client has 
> to do to work with it correctly is *not make subclasses*. The library is 
> still usable. Conversely, if a library mistakenly prohibits subclassing, then 
> there are things a client *should* be able to do but cannot. The harm to the 
> users of a library is greater in this last case, because the ability to use 
> the library is compromised, and that diminishes their productivity.

If a client uses a library type which was never intended to be subclassed and 
subclasses it, the only options to take the library forward are to:
1. break the client
2. make that usage part of your supported API, and work around any issues that 
causes

I sympathize with app developers under deadlines. I wouldn’t mind Swift having 
an ‘escape hatch’ to let an app developer take responsibility and to then abuse 
a library’s access levels (if that were possible without restricting 
optimizations in the Swift compiler). But closed-by-default is safer, and lets 
poorly specified libraries evolve to be higher quality libraries without 
breaking existing usage that they never intended to support.

> We should not make “soft defaults” that tend to negatively impact the clients 
> of a library for the dubious benefit of enabling its author to procrastinate 
> on a basic design decision. If someone truly wants to publish a library with 
> a closed class, then we should support that. But it should be an intentional 
> decision, not a default.

If you were proposing the default for public classes be final rather than 
closed, I would be on-board with that line of thinking.

If you proposed that final was redundant and should go away, leaving just 
public and public open, I’d find that defensible (I think even with closed as a 
default, it is useful to indicate within am module that a class is subclassed 
or not) 

If you proposed that library authors should be forced into a decision, and 
should get a warning with a default of final until they label public classes as 
open or final or sealsed, I’d also be on-board with that option.

However, “open by default” is quite different than “final by default”. The 
default for structs and enums are final by default. Protocols are 
implementable, but they by definition should have defined semantics for doing 
so. Open-by-default subclasses have the potential to be modified in ways you 
were never expected, then handed back to you. Exposing a class the same way you 
would expose a struct, enum, or protocol and having it directly impact your 
resiliency because you forgot to also add “final” is just not as safe. I think 
the SE-0117 discussion covered this *very* well.

> Motivation – Rethinking ‘private’
> 
> Now let us return to ‘private’, which as discussed earlier should be the only 
> modifier that is tighter than ‘internal’. The purpose of ‘private’ is to 
> enable encapsulation of related code, without revealing implementation 
> details to the rest of the module. It should be compatible with using 
> extensions to build up types, and it should not encourage overly-long files.

As said before, I think this is an incorrect conclusion. 

My personal thinking has been swayed over the last few months, and I’ve gone 
from vehemently opposing private to seeing merit in it. I believe my opposition 
was not against scoped private, but that it both kept fileprivate as a required 
access level and a redundant one, because it was not part of a larger change. 

Swift need a modifier between private and internal, and “fileprivate” is 
neither the feature we want, nor the spelling. Fileprivate should have been 
replaced with a more appropriate access level between private and internal, to 
represent ‘friend” relationships. I think we may be now looking at doing that, 
with submodules. 

My tact is that keywords should first and foremost document developer intent.

If I were to say which members of a type *should* be private, it would be the 
members that are not properly designed to uphold class invariance. Allowing 
access to write a negative integer to a property that should only ever be 
positive would be a good example. Calling a member without holding a lock or 
first dispatching onto the right GCD queue would be another.

In that context, a private which restricts even same-file, build-up extensions 
could be a positive language feature, because the extensions are being built 
using the safer interface. This is a trade-off, as some interfaces (NSCoding is 
a prime example) may require access to members otherwise made private.

> The natural definition, therefore, is that ‘private’ should mean “visible in 
> a small group of files which belong together as a unit”. Of course Swift does 
> not yet have submodules, and is not likely to gain them this year. However, 
> if we say that each file is implicitly its own submodule unless otherwise 
> specified, then the model works. In that view, ‘private’ will mean “visible 
> in this submodule”, and for the time being that is synonymous with “visible 
> in this file”.

I think internal is a more appropriate visibility within a submodule.

-DW

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to