Just scanned it, but my first impression is: LOVE IT! IMO this is the way to go.
Though some of the details will no doubt be improved. I will read it in more depth coming WE. Regards, Rien Site: http://balancingrock.nl Blog: http://swiftrien.blogspot.com Github: http://github.com/Balancingrock Project: http://swiftfire.nl > On 02 Mar 2017, at 20:58, Matthew Johnson via swift-evolution > <[email protected]> wrote: > > I’ve been continuing to think about how to provide clear and consistent > semantics for access control in Swift. This draft represents what I think is > the best way to accomplish that. It eliminates the current inconsistencies > and establishes a principled basis for the features we have today as well as > the enhancements we may need in the future. It does this with minimal > breaking changes. > > The draft is included below and can also be found here: > https://github.com/anandabits/swift-evolution/blob/scope-bounded-capabilities/proposals/NNNN-scope-bounded-capabilities.md. > > I’m looking forward to everyone’s feedback. > > Matthew > > > A Consistent Foundation For Access Control: Scope-Bounded Capabilities > > • Proposal: SE-NNNN > • Authors: Matthew Johnson > • Review Manager: TBD > • Status: Awaiting review > Introduction > > This proposal introduces a consistent foundation for all access control in > Swift: scope-bounded capabilities. The existing access control features are > generalized with a single mechanism that unifies their semantics. This > unified mechanism eliminates the inessential complexity and inconsistency of > the current system while expanding its utility. > > Swift-evolution thread: Discussion thread topic for that proposal > > Motivation > > The new access control features in Swift 3 have proven to be extremely > controversial. The most common refrain is that we need a more simple system. > In order to accomplish this we need to do more than tweak the system we > already have. We need to revisit the foundation of the system itself. > > Simple Made Easy > > Rich Hickey gave a fantastic talk called [Simple Made > Easy])(https://www.infoq.com/presentations/Simple-Made-Easy). In this talk > Rich explores the etymology and relationship of the words "simple", > "complex", and "easy". The meanings he explores are: > > • Complex: entangled, intertwined, interleaved, braided together > • Simple: one strand, single focus, disentangled > • Easy: familiar, nearby, readily at hand > The central point Rich makes in this talk is that when a design entangles two > orthogonal concepts complexity is the result. He coins the term "complect" to > refer to this kind of inessential complexity. This complexity can be removed > by disentangling the concepts. Instead of "complecting" independent concerns > we can compose them. > > The result is a simpler system. It is simpler because independent concepts > can be considered and understood independently of each other. > > The composition of independent concerns also results in a more flexible > system. When orthogonal concepts are entangled it is more difficult to extend > the system to meet future needs. One concept cannot be extended independently > of the other. It is not possible to make independent decisions about what > should be orthogonal aspects of the design. > > Rich believes that the programming community is often too quick to reach for > an immediately "easy" solution. Unfortunately, the "easy" solution often > entangles concepts and are therefor actually complex. He suggests that we > firstdesign a simple (i.e. disentangled) solution and then layer ease of use > and familiarity on top, thus the title "Simple Made Easy". > > Two orthogonal concepts > > The access control system in Swift 3 incorporates two orthogonal concepts: > availability and capability. Availability is what immediately comes to mind > when one thinks of access control: a symbol is either available or it is not. > Capability is more nuanced. It refers to what you can do with that symbol. > > Each declaration supports a basic capability which is always available when > the symbol itself is available. Many declarations also offer additional > capabiities (such as the ability to inherit, override, set a property, etc). > These additional capabilities may be less available than the symbol itself. > > In Swift, availability is always specified in terms of a scope. Swift does > not currently have a consistent way to talk about capabilities. Thus far we > have introduced new syntax every time we wish to distinguish the availabiltiy > of an additionalcapability from that of the symbol itself: > > • open vs public access modifiers classes and methods > • Access modifier parameterization for setter availability: private(set) > • The @closed attribute which has been discussed as a way to specify > non-resilient enums in Swift 4* > It is clear that we need to be able to talk about not just basic > availability, but also capabilities. It would be very nice if we had one > consistent way to do this. This can be accomplished by composing the concepts > of availability and capability into the notion of a scope-bounded capability. > > *@closed would lie outside the access control system proper. It is included > for the sake of completeness. It is also included to demonstrate how the > language currently lacks a clear and obvious way to specify new capability > bounds when they are arise. > > Problems with Swift's access control system > > Swift's current access control system can has several problems. > > Inconsistency > > As noted above, the ways additional capabilities are bounded is inconsistent. > The semantics of public are also inconsistent. > > Internal default > > The Swift evolution community has adopted the principle that nothing should > be available outside a module without an explicit declaration of intent by a > library author. This is an excellent default which protects library authors > against making an error of omission that would require a breaking change to > correct. Unfortunately this principle has not been consistently applied. > > public > > In most cases public only provides access to the basic capability a > declaration offers. This is true by definition for declarations do not offer > additional capabilities but it is also true for classes (with respect to > inheritance) and class methods (with respect to overrides). > > However, there are three cases where public currently provides access to > additional capabilities: > > • public var allows access to the setter > • public enum allows exhaustive switch > • public protocol allows new conformances to be introduced > It is not currently possible to declare resilient enums or closed protocols > but both have received significant discussion. Further, resilient enums need > to be supported before ABI stability is declared. A consistent access control > system would treat these as independent capabilities that are not made > available with a simple public declaration. > > private and fileprivate > > The most heavily debated aspect of the changes to the access control system > in Swift 3 is without question the change in meaning of private to be the > current lexical scope and the renaming of the file-level scope to > fileprivate. This change was made with the idea that a lexically scoped > private would prove to be a good "soft default" for a less-than-module > availability bound. While many users appreciate the semantics of a > scope-based access modifier it has not proven to be a good "soft default" and > therefore does not deserve the name private. > > Extensions > > In languages without extensions lexically scoped availability is equivalent > to type-based availability for members of a type. In such a language it could > make a reasonable default. Swift is not such a language. > > Using several extensions on the same type within the same file is an > extremely common Swift idiom. This idiom is not well supported by a "soft > default" of scope-based availability. The tension between a pervasive idiom > and the "soft default" leads to confusion about when scope-based a > availability is appropriate, and often an overuse of the feature. It also > leads to the need to use fileprivate much more frequently than is desirable > for such an awkward keyword. > > Types and members > > A "soft default" should not have subtle behavior that has the potential to > confuse beginners. Most beginners would expect Foo and bar in the following > example to have the same visibility. This was true in Swift 2 but it is not > true in Swift 3. > > private struct Foo > { > > private var bar = 42 > > } > > An advanced feature > > Lexically scoped availability has important uses such as preserving > invariants. All access to invariant-related state can be routed through basis > methods which access the state carefully without violating invariants, even > when that access happens in an extension in the same file. We should not > abandon this tool but it should not be the "soft default". It is best > reserved for specific use cases where the guarantee it offers is important to > correctess of the software. > > Essential and inessential complexity > > The inconsistencies noted above and a bad "soft default" of private are all > forms of inessential complexity. This makes Swift's access control system > more difficult to understand and use than it needs to be and causes confusion. > > At the same time the essential complexity of capabilities that are bounded > independent of basic symbol availability is not explicitly acknowledged and > embraced. This also makes the access control system more difficult to > understand and use than it should be. Users are not taught to think in terms > of independently bounded capabilities. This is a concept that could be > learned once and applied generally if it was more visible in the language. > > Proposed solution > > The proposed solution is to establish a semantic foundation for access > control that is simple in the sense of composing rather than interleaving > independent concerns. The solution is made easy by defining familiar names in > terms of this foundation while preserving the semantics Swift users expect > them to have. It is consistent in its use of a single mechanism for bounding > capabilities and its default of internal for all capabilities. > > Scope-bounded capabilities > > All access control is defined in terms of a parameterized access modifier > that allows the user to specify a capability and a scope that bounds that > capability. > > // The scope of the setter is the current file. > scope(set, file) var foo = 42 > Each parameter has a default argument. The default argument for the > capability is simply the basic capability the declaration provides. For a > variable this is the getter, for a method it is the ability to call the > method, for a type it is the ability to use the type and so on. The default > argument for the scope is the current lexical scope. > > // The scope of the getter is the current lexical scope. > // This is equivalent to `private var foo = 42` in Swift 3. > scope var foo = 42 > > > > // The scope of the getter is the current file. > scope(file) var bar = 42 > > > > // The scope of the setter is the current lexical scope. > scope(set) var baz = 42 > The scope of the basic capability implicitly bounds additional capabilities: > if basic use of a symbol is not available it is not possible to anything with > that symbol. This is similar to the existing rule that a type implicitly > bounds the availability of all symbols declared within its scope: a public > property of an internal type is not available outside the module because the > type itself is not available. > > Aliases > > This modifier is simple (in the sense defined above), general and powerful. > However it is also unfamiliar, often slightly verbose, and offers a very wide > configuration space. Familiar aliases are provided as "soft defaults" which > are recommended for common use cases. > > These aliases introduce no additional semantics. Once a user understand > scopes, capabilities and how they compose to produce scope-bounded > capabilities the user also has the ability to understand all aliases we > introduce. Tools could even make the definition of the alias available to > remind the user of its underlying meaning (similarly to they way Xcode allows > a user to command-click to see a symbol definition). > > These aliases are defined in terms of the parameterized scope modifier: > > • private(capability) = scope(capability, file) > • internal(capability) = scope(capability, submodule) > • public(capability) = scope(capability, everywhere) > • open = scope(inherit, everywhere) > • open = scope(override, everywhere) > • final = scope(inherit, nowhere) > • final = scope(override, nowhere) > • total = scope(switch, everywhere) > private reverts to the Swift 2 semantics. scope with no parameters has > semantics equivalent to that of private in Swift 3. > > internal is specified in terms of submodule and is equivalent to module scope > until submodules are introduced. It is specified this way to indicate the > intent of the author should submodules be added. > > total is a placholder subject to bikeshedding. total enum provides semantics > equivalent to public enum. public enum receives the semantics of resilient > enums. If a suitable shorthand is not identified the slightly longer > public(switch) enum can be used to specify an enum which supports exhaustive > switch outside the module. > > open, final and closed are overloaded based on the kind of declaration they > are applied to. > > Scopes > > The hierarchy of scopes is as follows: > > • nowhere > • lexical > • TypeName > • extension > • file > • submodule > • SubmoduleName > • module > • everywhere > The name of any ancestor type or submodule of a declaration, including the > immediately containing type or submodule, form the set of valid user-defined > scope references. > > Including nowhere allows us to define final in terms of this system. It also > allows us to model all properties and functions with the same set of > capabilities: the setter of a read only property is automatically bounded to > nowhere and the override capability of a function that is not a class method > is automatically bounded to nowhere. > > Allowing users to reference any ancestor scope introduces affords advanced > users a degree of control that is not possible in the current access control > system. If submodules are introduced into Swift this additional control will > be especially useful as a means to facilitate bounded collaboration between > peer submodules allowing them to communicate in ways not available to the > rest of the module. > > Capabilities > > The capabilities available depend on the kind of declaration an access > modifier is applied to. All declarations offer a basiccapability that is > always available when the declaration itself is available. The basic > capability is specified by default when the scope modifier or a parameterized > alias is used without an explicit capability argument. Some declarations also > offer additional capabilities which may have an independent bound applied to > them. > > Properties and subscripts > > • get (the basic capability) > • set (for readwrite properties only) > • override (for class properties only) > Functions and initializers > > • call (the basic capability) > • override (for class methods only) > Types > > • use (the basic capability) > • inherit (for classes) > • switch (for enums) > Protocols > > • use (the basic capability) > • conform > Extensions and typealiases > > • use (the basic capability) > Scalable in the future > > As the language grows the mechanism of scope-bounded capabilities can be > extended in an obvious way to meet the needs of future declarations and > capabilities. Users are only required to learn about the new declaration or > capability that was introduced. Their existing knowledge of the scope-bounded > capability access control system is immediately applicable. > > Detailed design > > Rules > > The rules which make up the essential complexity in Swift's access control > system are: > > • The default scope for all capabilites a declaration offers is > module-wide (or submodule-wide in the future). > • The scope of a capability may be modified by an explicit access > modifier. > • The scope of an additional capability is implicitly bounded by the > scope of the basic capability. > • The scope of an additional capability may not be explicitly specified > as greater than that of the basic capability. > • If no scope is explicitly provided for the basic capability and an > additional capability is specified to be available outside the (sub)module > the basic capability is also given the same availability. > • The scope of a declaration (including all capabilities) may be > bounded by the declaration of ancestor. > • The scope of a declaration may not be greater than the scope of the > capabilities necessary to use that declaration: if you can't see a parameter > type you can't call the function. > Most of these rules already exist in Swift's access control system. There is > one change and one addition: > > • The first rule changes the availability of the additional capability > of public readwrite properties, protocols and enums. > • The fifth rule affords shorthand for implicitly making the basic > capability public when an additional capability is also made public. > Grammar > > The changes to the access modifier grammar are as follows: > > access-level-modifier → scope | scope( capability-specifier ) | scope( > scope-specifier ) | scope( capability-specifier , scope-specifier ) > access-level-modifier → private | private( capability-specifier ) > access-level-modifier → internal | internal( capability-specifier ) > access-level-modifier → public | public( capability-specifier ) > access-level-modifier → open > access-level-modifier → final > access-level-modifier → total > > scope-specifier → nowhere | extension | file | submodule | module | > everywhere | identifier > capability-specifier → set | inherit | override | conform | switch > > Playground > > A Swift playground that includes a prototype of the basic scope-bounded > capability access modifier as well as the aliases is availabe here. > > Future possibilities > > Scope-bounded capabilities are able to express set-only properties and > override-only methods with a minor change to the rules of the system. These > features have been requested on the list in the past. In the case of > override-only methods there are known examples in Apple's frameworks. > Allowing support for these would add some complexity to the model and is not > essential to establishing a consistent basis for the existing access control > feature. > > Source compatibility > > This proposal will not cause any Swift 3 source to fail to compile but will > produce different behavior in three cases. In all cases a mechanical > migration is possible. > > public var > > This proposal removes the availability of the setter of a public var outside > the module, requiring public(set) var to expose the setter. This requires a > migration of existing code. We could ease this transition with a deprecation > warning in one release and then introduce the semantic change in the > following release. > > public enum > > This proposal removes the availability of exhaustive switch from public > enums. Non-resilient enums will need to be declared as a total enum (or the > equivalent keyword chose after bikeshedding) or public(switch) enum if we > choose not to introduce an alias for this semantic. As with public var, a > deprecation warning and deferred semantic change could be used to ease the > transition. > > public protocol > > This proposal requires protocols conformable outside the module to use the > open protocol alias rather than public protocol. Visible but not conformable > protocols are out of scope for Swift 4. This means that in Swift 4 open > protocoland public protocol could share the same semantics with a deprecation > warning on public protocol telling users to use open and that the semantics > of public protocol will be different in the future. We could remove support > for public protocol in a point release, reserving it until we introduce the > ability for a protocol to be visible but not conformable. > > Effect on ABI stability > > If this proposal impacts ABI stability it would be in the area of runtime > metadata or introspection. Input on this topic is welcome. > > Effect on API resilience > > This proposal does not impact API reslience. The proposed solution recasts > some existing features but does so in a way that should be backwards > compatible. No existing semantics are changed, only how those semantics are > stated syntactically. > > Alternatives considered > > The primary alternative is to continue using the ad-hoc approach to meeting > our access control needs. There have been many different ideas about how we > might be able to simplify the current system by removing or slightly tweaking > some of the existing features. The author believes this approach will not be > able to meet future needs well and will continue to be burdened by the > accumulation of inessential complexity. This situation will get worse, not > better, as new features are added to the language. > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution _______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
