Types have a ton of different implicit/explicit “API”, and access control 
modifiers often implicitly define that API:

- API for everyone to use when dealing with your concrete type (“public”)
- API for module code when you have related, coupled types that need a higher 
degree of knowledge (“internal”)
- API for within the implementation of the type itself (“fileprivate”/“private”)
- API to other code to interact with your type in a more general manner 
(protocol implementation)
- You also have two versions of each of these - instance and static/class-level 
properties/methods (including initializers)
- Each of these can also have stability aspects - which versions of a framework 
support the API, whether the API is deprecated or obsoleted, etc. (exposed 
partially today via “#available")

And classes add even more!:
- Whether subclassing is allowed (‘final’)
- API for subclasses to use for their implementation, but not meant for general 
usage (typically “protected")
- API which subclasses are allowed to override to implement new logic (“open")
- API which subclasses are forbidden to override because they define business 
logic used by coupled code ("final"/"closed")
- API which subclasses are required to override (typically “abstract" base 
classes - Swift and Objective C seem to prefer Delegates instead)

(I’m probably forgetting a few)

So access levels serve three main purposes:
1. to define these API so that a developer interacting with your type knows 
what is or is not (for instance) a subclass knows what it is or is not allowed 
to change, code using your types know what is or is not safe to call, etc.
2. to try to enforce these API to be used  only by the intend audience
3. to prevent reliance on implementation details as a stable API

Obviously not all of these cases need compiler-enforcement of the API - nor 
could you have a simple enough system for general purpose consumption which 
attempted to do so. In the case language features do not document the 
stakeholders or behavior of the API, regular documentation and processes should 
attempt to do so. 

This IMHO was the majority of the argument against SE-0025 - that if you are 
already in the same file, you must know the implementation details well enough 
to know what is or is not safe API. If developers were putting too much code 
within a single file was a case for a level above fileprivate, not below it. 
This why I personally pushed to defer until there was a submodule design.

The public/internal/private model is nice because it mirrors code locality, and 
thus is focused on enforcement of safety. If some other code depends on 
implementation details of my type that I don’t want to expose to the world, 
that code is going to be in the same module or even the same file. Hiding 
implementation details is enforcement for safety.

Classes obviously provide an explosion of complexity in defining behavior 
because of the additional relationship with sub- and super-classes. I generally 
push people away from designing their packages to rely on subclassing (instead 
preferring protocols and aggregation) because keeping this complexity straight 
and having a good design that reduces coupling is so difficult when dealing 
with subclassing.

-DW
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to