> On Mar 3, 2017, at 4:05 AM, Rien <r...@balancingrock.nl> wrote:
> 
> 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.

Awesome, thanks!  I have already received a lot of great feedback (thanks 
Jaden!) and will be posting a second draft that incorporates it as soon as I 
have a chance to make the updates.

> 
> 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 
>> <swift-evolution@swift.org> 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
>> swift-evolution@swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 

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

Reply via email to