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
