I agree - both will need to be addressed in the future to make the language 
complete. There are some valid points, however, in the blog post - would you 
allow extensions to access protected variables and potentially expose them to 
other classes? I.e:

/// Class in module A:
class View {
        protected func layoutSubviews()
}

/// In module B:
extension View {
        func doLayoutSubviews() {
                self.layoutSubviews()
        }
}

Or would you disallow access from extensions to prevent this kind of abuse?

> On May 29, 2016, at 11:19 AM, Goffredo Marocchi <[email protected]> wrote:
> 
> 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