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