Without wither abstract classes or a protected access modifier, the status quo, 
that kind of ExceptionThisMethodShouldBeOverridden are really ugly bad code 
there is no alternative to beyond a religious stop using classes and sub 
classing... did you not know about your pop saviour ;)? 

Sent from my iPhone

> On 29 May 2016, at 08:38, Charlie Monroe via swift-evolution 
> <[email protected]> wrote:
> 
> Ditto - I would love to be able to disallow non-subclasses 
> accessing/modifying some variables.
> 
> Though, I'm not sure what would be the stand on this from the core team - 
> according to Apple's blog they've already considered protected access level:
> 
> https://developer.apple.com/swift/blog/?id=11
> 
> Charlie
> 
> 
>> On May 29, 2016, at 7:56 AM, Riley Testut via swift-evolution 
>> <[email protected]> wrote:
>> 
>> Unless I'm missing something Brent, your suggestions still wouldn't allow 
>> the developer to provide a public class in a module designed to be 
>> subclassed by clients in another module and access these "private" details, 
>> which is a real problem I'm having in my current project.
>> 
>> I have a framework which provides an abstract model object designed to be 
>> subclassed (and yes it has to be a class and not a struct for a multitude of 
>> reasons ๐Ÿ˜›) by clients of the framework. There are several convenience 
>> methods + properties I have exposed for subclasses to use, but they should 
>> really be implementation details; a client using these model objects should 
>> not have to know about them. Even worse, several of the properties are 
>> mutable so the subclasses can modify them, but they certainly should *not* 
>> be modified by anything else.
>> 
>> Right now, I'm limited to simply commenting something akin to "DO NOT CALL" 
>> next to these methods/properties, which definitely goes against Swift's 
>> safety focus. For these reasons, I'm 100% in support of a protected access 
>> control modifier.
>> 
>>> On May 28, 2016, at 8:11 PM, Brent Royal-Gordon via swift-evolution 
>>> <[email protected]> wrote:
>>> 
>>> To begin with, I'm not a fan of `protected` access. But even leaving that 
>>> aside, I have a few questions and critiques.
>>> 
>>>> A common case is the UIView from UIKit. Many developers are tempted to 
>>>> make this call:
>>>> 
>>>> view.layoutSubviews()
>>>> The documentation says: "You should not call this method directly. If you 
>>>> want to force a layout update, call the setNeedsLayoutmethod instead to do 
>>>> so prior to the next drawing update. If you want to update the layout of 
>>>> your views immediately, call the layoutIfNeeded method."
>>> 
>>> This example is illuminating in several ways.
>>> 
>>> * The rule is not simply that "only the class should call 
>>> `layoutSubviews()`"; it is effectively "*you* should never call 
>>> `layoutSubviews()` except when `super`ing up from your override". Calling 
>>> `layoutSubviews()` from `insertRows(at:)` is just as much a mistake if 
>>> `insertRows(at:)` is part of the class as if it is not. So isn't 
>>> `protected` insufficiently strict to properly serve this use case?
>>> 
>>> * At the same time, something outside `layoutSubviews()` has to be able to 
>>> call `layoutSubviews()`. In the case of UIKit, though, that "something" is 
>>> always within UIKit itself, never outside it. So should `protected` have a 
>>> "bottom", a level below which calls are unrestricted? For instance, in 
>>> UIKit's case you might have `protected fileprivate`, meaning "anything up 
>>> to `fileprivate` has unrestricted use; anything above that can override and 
>>> `super` up from its override, but not use it any other way".
>>> 
>>>  protected fileprivate func layoutSubviews()
>>> 
>>> * `layoutSubviews()` is also something you should probably always `super` 
>>> up to. Have you considered addressing `super` requirements at all?
>>> 
>>> In short, is a traditional `protected` really the feature you want to 
>>> handle this use case, or would a very different design actually suit it a 
>>> lot better?
>>> 
>>>> When declarated by a class the protected member will be visible to the 
>>>> class itself and all the derived classes.
>>> 
>>> In what scope? The same as the class?
>>> 
>>> Is there not room for, for instance, "usable without restriction in this 
>>> file, override-only in the rest of this module, invisible outside it"? For 
>>> instance, `internal(protected) fileprivate`, or perhaps `internal(override) 
>>> fileprivate`? `layoutSubviews()` might then be `public(override) 
>>> fileprivate`โ€”the ability to override is public, the ability to use it 
>>> unrestricted is filewide.
>>> 
>>>  public(override) fileprivate func layoutSubviews()
>>>  internal(override) fileprivate func privateSubclassingHook()
>>> 
>>>> public protected(set) var x = 20
>>> 
>>> Of course, that might be difficult to combine with the `(set)` syntax. 
>>> `public(set: override)`, maybe? With, for instance, `public internal(set: 
>>> override) private(set)` if you want the property's getter public and its 
>>> setter overridable internally and callable in private scope.
>>> 
>>>  public(override) fileprivate func layoutSubviews()
>>>  internal(override) fileprivate func privateSubclassingHook()
>>>  public(get, set: override) internal(set) var x = 20
>>> 
>>> But there's something about this that's starting to seem a little rotten. I 
>>> think the problem is that we're not really trying to widen the ability to 
>>> override, we're trying to restrict the ability to call. Let's try 
>>> restructuring along those lines:
>>> 
>>>  public fileprivate(call) func layoutSubviews()
>>>  internal fileprivate(call) func privateSubclassingHook()
>>>  public internal(set: call) var x = 20
>>> 
>>> That seems much cleaner to me.
>>> 
>>>> If the member is declared as final then it will be visible but not can be 
>>>> overrided by the derived classes. Just like it works with other access 
>>>> levels.
>>> 
>>> With the "overridable but otherwise unusable" conception I'm suggesting, 
>>> this would not be the case, of course.
>>> 
>>>> Protocols
>>>> 
>>>> Protocols do not declare access level for their members. So the protected 
>>>> access level is not applicable here.
>>> 
>>> But `protected` is quite different from other access levels; it does not 
>>> limit the visibility of the symbols, but rather their use. And protocols 
>>> face the same sort of problem as classes, where certain members are 
>>> essentially override hooks and shouldn't be called directly outside a 
>>> particular scope.
>>> 
>>> So I think we ought to allow `accesslevel(call)`, but not a plain 
>>> `accesslevel`:
>>> 
>>>  public fileprivate(call) func layoutSubviews()
>>>  internal fileprivate(call) func privateSubclassingHook()
>>>  public internal(set: call) var x = 20
>>>  internal(call) func protocolConformanceHook()
>>>  fileprivate(set: call) var onlyProtocolSetsThis: Int { get set }
>>> 
>>>> Extensions
>>>> 
>>>> Extensions will not be able do be protected nor their members.
>>> 
>>> This is very vague. There are several things extensions might try to do 
>>> with protected members:
>>> 
>>> * Declare new ones
>>> * Override existing ones
>>> * Call existing ones
>>> 
>>> Which of these, if any, are permitted? Why?
>>> 
>>> In my conception, I would permit extensions to behave as the type they 
>>> extended did. Extensions could declare new members with restricted calling 
>>> and override existing ones. They would not be able to call, except when 
>>> supering from an override, unless they were within scope of the `call` 
>>> access control. In other words, they'd behave just like any other code at 
>>> that location. That's how we want extensions to work.
>>> 
>>>> But nested declarations will be allowed, so this code will compile:
>>>> 
>>>> // We can declare a protected class (or struct, enum, etc.) if
>>>> // and only if they are nested inside other type.
>>>> public class MyPublicClass {
>>>> protected 
>>>> class MyProtectedClass {
>>> 
>>> What does it mean to "use" a protected class, though? Clearly you can call 
>>> its methods, if only through AnyObject or a non-protected superclass or a 
>>> protocol it conforms to. Does it mean you can't instantiate it? Does it 
>>> mean you can't subclass it? Does it mean you can't call methods that aren't 
>>> on its supertypes? All of the above? None?
>>> 
>>> One more thing that didn't come up: Testability. I believe that importing a 
>>> module with `@testable` should disable its call restrictions, even ones 
>>> inherited from outside that module. Thus, even if *you* cannot call your 
>>> `layoutSubviews()`, your test suite can.
>>> 
>>> So, in short, my counter-proposal is:
>>> 
>>>  public fileprivate(call) func layoutSubviews()
>>>  internal fileprivate(call) func privateSubclassingHook()
>>>  public internal(set: call) var x = 20
>>>  internal(call) func protocolConformanceHook()
>>>  fileprivate(set: call) var onlyProtocolSetsThis: Int { get set }
>>> 
>>> In other words:
>>> 
>>> * There is a new aspect of the member, `call`, which controls the ability 
>>> to actually call the member, as opposed to overriding it. No `call`, no 
>>> calling (except when `super`ing up from an override).
>>> 
>>> * `call` is used in combination with one of the existing access modifiers: 
>>> `public(call)` `internal(call)` `fileprivate(call)` `private(call)`. 
>>> `call`'s visibility is always less or equal to the member itself.
>>> 
>>> * To control the callability of a setter independently from both the getter 
>>> and the overridability of the setter, use `set: call`.
>>> 
>>> * Extensions behave just like type definitions at the same location with 
>>> regards to `call`.
>>> 
>>> * Protocols can use access modifiers with `call` to prevent unauthorized 
>>> code from calling a member. The access control level to implement a member 
>>> continues to be as wide as the access control level of the protocol itself.
>>> 
>>> * `@testable` disables `call` restrictions on the types it imports, so the 
>>> test suite can call any visible member, even ones inherited from other 
>>> modules.
>>> 
>>> * There should probably also be some sort of "super required" 
>>> warning/error, but this is an orthogonal feature and can be left for a 
>>> separate proposal.
>>> 
>>> I think that feature will be closer to the one you actually *want*, as 
>>> opposed to the one that other languages have cargo-culted from SIMULA-67.
>>> 
>>> -- 
>>> Brent Royal-Gordon
>>> Architechies
>>> 
>>> _______________________________________________
>>> 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
> 
> _______________________________________________
> 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

Reply via email to