[Proposal] Protected Access Level


(Vanderlei Martinelli) #1

Hello.

This is the first draft. I'd like to know your opinion about it.

(I know that this subject could have been discussed before. If so, please
indicate me the correct thread to follow and interact.)

Regards,

Vanderlei Martinelli

···

---

Introduction

Protected access level will enable entities to be used within the container
type and by derived types only.
Motivation

Today Swift has three access levels (public, internal and private), but
lacks a way to describe a member that can be only visible to its type or
derived types.

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."

But yes, you should call this method directly if you are subclassing the
view and needs to perform additional layout to its subviews ("subclasses
can override this method as needed"):

public override func layoutSubviews() {
    // We are calling the super method directly here.
    super.layoutSubviews()

    // Do more adjustments to this view's subviews...}

So, yes, we can call this method directly when subclassing, but the Swift
compiler will not prevent you from do this when not subclassing or from any
other foreign class. It will not even issue a warning.

In Objective-C problems like this are usually "solved" my adding a kind of
"protected" header (.h) that is intended to be included only when the
developer is subclassing. In Swift we do not have headers, but we have the
new access level model. So, if the declaration of this method was...

protected func layoutSubviews()

... no one outside the class or derived classes would be allowed to call
this method directly.

Of course, there are other cases in the Cocoa frameworks and there are many
other cases when we are developing software in Swift that the protected access
level would be very usefull.
Proposed solution

Create the protected access level.
Detailed designReference Types (classes)

When declarated by a class the protected member will be visible to the
class itself and all the derived classes.

// BaseClass.swiftpublic class BaseClass {
    public protected(set) var x = 20
    protected let y = 10

    protected func doSomething() {
        // ...
    }}
// DerivedClass.swiftpublic class DerivedClass: BaseClass {
    protected override doSomething() {
        self.x = 10 * self.y
    }}

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.
Value Types (structs, enums, etc.)

Value types cannot have derived types. In this case the protected access
level does not make sense and will not be allowed in their members.
Protocols

Protocols do not declare access level for their members. So the
protected access
level is not applicable here.
Extensions

Extensions will not be able do be protected nor their members.
Special Note

The protected access level can only be applied to classes, structs and
other types when nested inside other type. So the following code will not
compile:

// ERROR: A first level class cannot be protected.
protected class MyProtectedClass {
    /// ...}

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 {
        /// ...
    }}
// Since `MyProtectedClass` is nested and protected we// can have
access to it here.public class MyDerivedClass: MyPublicClass {
    public func doSomething() {
        let c = MyProtectedClass()

        /// ...
    }}

Impact on existing code

The imported Cocoa frameworks may have annotations on the "Objective-C
side" that will inform if one member is protected. If this will be case
(for the layoutSubviews method, as example), only code accessing these now
protected members outside the owner class and its derived classes will have
to change (currently this can be considered an error, anyway).

Any other code will be not impacted by this feature as it is new to the
language.
Alternatives considered

Do not add the protected access level to the current model.

---


(Brent Royal-Gordon) #2

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


(Xiaodi Wu) #3

Seems entirely reasonable to me. POP aficionados may object, but I think
the pros and cons of this type of access are well trodden terrain and I
think it's a net win to have this available in Swift going forward.

···

On Sat, May 28, 2016 at 18:53 Vanderlei Martinelli via swift-evolution < swift-evolution@swift.org> wrote:

Hello.

This is the first draft. I'd like to know your opinion about it.

(I know that this subject could have been discussed before. If so, please
indicate me the correct thread to follow and interact.)

Regards,

Vanderlei Martinelli

---

Introduction

Protected access level will enable entities to be used within the
container type and by derived types only.
Motivation

Today Swift has three access levels (public, internal and private), but
lacks a way to describe a member that can be only visible to its type or
derived types.

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."

But yes, you should call this method directly if you are subclassing the
view and needs to perform additional layout to its subviews ("subclasses
can override this method as needed"):

public override func layoutSubviews() {
    // We are calling the super method directly here.
    super.layoutSubviews()

    // Do more adjustments to this view's subviews...}

So, yes, we can call this method directly when subclassing, but the Swift
compiler will not prevent you from do this when not subclassing or from any
other foreign class. It will not even issue a warning.

In Objective-C problems like this are usually "solved" my adding a kind of
"protected" header (.h) that is intended to be included only when the
developer is subclassing. In Swift we do not have headers, but we have the
new access level model. So, if the declaration of this method was...

protected func layoutSubviews()

... no one outside the class or derived classes would be allowed to call
this method directly.

Of course, there are other cases in the Cocoa frameworks and there are
many other cases when we are developing software in Swift that the
protected access level would be very usefull.
Proposed solution

Create the protected access level.
Detailed designReference Types (classes)

When declarated by a class the protected member will be visible to the
class itself and all the derived classes.

// BaseClass.swiftpublic class BaseClass {
    public protected(set) var x = 20
    protected let y = 10

    protected func doSomething() {
        // ...
    }}
// DerivedClass.swiftpublic class DerivedClass: BaseClass {
    protected override doSomething() {
        self.x = 10 * self.y
    }}

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.
Value Types (structs, enums, etc.)

Value types cannot have derived types. In this case the protected access
level does not make sense and will not be allowed in their members.
Protocols

Protocols do not declare access level for their members. So the protected access
level is not applicable here.
Extensions

Extensions will not be able do be protected nor their members.
Special Note

The protected access level can only be applied to classes, structs and
other types when nested inside other type. So the following code will not
compile:

// ERROR: A first level class cannot be protected.
protected class MyProtectedClass {
    /// ...}

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 {
        /// ...
    }}
// Since `MyProtectedClass` is nested and protected we// can have access to it here.public class MyDerivedClass: MyPublicClass {
    public func doSomething() {
        let c = MyProtectedClass()

        /// ...
    }}

Impact on existing code

The imported Cocoa frameworks may have annotations on the "Objective-C
side" that will inform if one member is protected. If this will be case
(for the layoutSubviews method, as example), only code accessing these
now protected members outside the owner class and its derived classes will
have to change (currently this can be considered an error, anyway).

Any other code will be not impacted by this feature as it is new to the
language.
Alternatives considered

Do not add the protected access level to the current model.

---

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


(Riley Testut) #4

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


(Vanderlei Martinelli) #5

Thank you all for your comments. :slight_smile:

Well... My goal is to keep the thing really simple and do not start a new
"OOP x POP" (or "something" x "other thing") war.

"Protected" access level is not a new concept at all (except for the Swift
language), so I did not propose anything preposterous.

Of course in the Swift of my dreams we also have "abstract" access level
modifier, "protected" access level, *real* "private" access level and
"file" access level modifier (along with many, many other things, of
course). But this proposal is not about this. It is only about include the
"protected" access level.

There is, however, something that I need to get off my chest: I really
would like to have the freedom to go to the depths with protocols as well
with classes. I work in real apps everyday that uses Cocoa frameworks
(based on classes) and these apps must be shipped and I like them well
written. Maybe am I insane for proposing a better support for classes in
Swift? If so, this explains why every time I suggest better support for
classes in Swift there is an endless discussion and someone proclaims the
death of OOP and is it. OK... Maybe someday we will not have more classes
in Swift. Until there: the current language status is the best way to
handle OOP in Swift? Or is there a better way? I think there is.

Regards,

Vanderlei Martinelli

···

On Sat, May 28, 2016 at 7:52 PM, Vanderlei Martinelli < vmartinelli@alecrim.com> wrote:

Hello.

This is the first draft. I'd like to know your opinion about it.

(I know that this subject could have been discussed before. If so, please
indicate me the correct thread to follow and interact.)

Regards,

Vanderlei Martinelli

---

Introduction

Protected access level will enable entities to be used within the
container type and by derived types only.
Motivation

Today Swift has three access levels (public, internal and private), but
lacks a way to describe a member that can be only visible to its type or
derived types.

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."

But yes, you should call this method directly if you are subclassing the
view and needs to perform additional layout to its subviews ("subclasses
can override this method as needed"):

public override func layoutSubviews() {
    // We are calling the super method directly here.
    super.layoutSubviews()

    // Do more adjustments to this view's subviews...}

So, yes, we can call this method directly when subclassing, but the Swift
compiler will not prevent you from do this when not subclassing or from any
other foreign class. It will not even issue a warning.

In Objective-C problems like this are usually "solved" my adding a kind of
"protected" header (.h) that is intended to be included only when the
developer is subclassing. In Swift we do not have headers, but we have the
new access level model. So, if the declaration of this method was...

protected func layoutSubviews()

... no one outside the class or derived classes would be allowed to call
this method directly.

Of course, there are other cases in the Cocoa frameworks and there are
many other cases when we are developing software in Swift that the
protected access level would be very usefull.
Proposed solution

Create the protected access level.
Detailed designReference Types (classes)

When declarated by a class the protected member will be visible to the
class itself and all the derived classes.

// BaseClass.swiftpublic class BaseClass {
    public protected(set) var x = 20
    protected let y = 10

    protected func doSomething() {
        // ...
    }}
// DerivedClass.swiftpublic class DerivedClass: BaseClass {
    protected override doSomething() {
        self.x = 10 * self.y
    }}

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.
Value Types (structs, enums, etc.)

Value types cannot have derived types. In this case the protected access
level does not make sense and will not be allowed in their members.
Protocols

Protocols do not declare access level for their members. So the protected access
level is not applicable here.
Extensions

Extensions will not be able do be protected nor their members.
Special Note

The protected access level can only be applied to classes, structs and
other types when nested inside other type. So the following code will not
compile:

// ERROR: A first level class cannot be protected.
protected class MyProtectedClass {
    /// ...}

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 {
        /// ...
    }}
// Since `MyProtectedClass` is nested and protected we// can have access to it here.public class MyDerivedClass: MyPublicClass {
    public func doSomething() {
        let c = MyProtectedClass()

        /// ...
    }}

Impact on existing code

The imported Cocoa frameworks may have annotations on the "Objective-C
side" that will inform if one member is protected. If this will be case
(for the layoutSubviews method, as example), only code accessing these
now protected members outside the owner class and its derived classes will
have to change (currently this can be considered an error, anyway).

Any other code will be not impacted by this feature as it is new to the
language.
Alternatives considered

Do not add the protected access level to the current model.

---


(Tino) #6

I'm not opposed to the idea of finer-grained access levels in general, but if the current system is extended, the change imho should be more "holistic":
Right now, class hierarchy has no effect on accessibility, and it feels odd to have access level that is different from the others.
Doing so would rise questions like "why it is not possible to have a method that can be called from the current module, and from subclasses?".

There are several scenarios that can't be modeled in Swift now, and that still couldn't be modeled with a new "protected"-level (especially methods that can't be called from outside, but can be overridden by subclasses could be very useful).


(Leonardo Pessoa) #7

If we're to introduce the protected visibility, I think we should as well make it automatically internal or introduce an explicit protected internal visibility. I've been through many cases while programming in C# 'where' this was handy (thou I cannot name one from memory right now; it's been so many years ago...)

···

-----Original Message-----
From: "Xiaodi Wu via swift-evolution" <swift-evolution@swift.org>
Sent: ‎28/‎05/‎2016 08:10 PM
To: "Vanderlei Martinelli" <vmartinelli@alecrim.com>; "swift-evolution" <swift-evolution@swift.org>
Subject: Re: [swift-evolution] [Proposal] Protected Access Level

Seems entirely reasonable to me. POP aficionados may object, but I think the pros and cons of this type of access are well trodden terrain and I think it's a net win to have this available in Swift going forward.

On Sat, May 28, 2016 at 18:53 Vanderlei Martinelli via swift-evolution <swift-evolution@swift.org> wrote:

Hello.

This is the first draft. I'd like to know your opinion about it.

(I know that this subject could have been discussed before. If so, please indicate me the correct thread to follow and interact.)

Regards,

Vanderlei Martinelli

---

Introduction
Protected access level will enable entities to be used within the container type and by derived types only.
Motivation
Today Swift has three access levels (public, internal and private), but lacks a way to describe a member that can be only visible to its type or derived types.
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."
But yes, you should call this method directly if you are subclassing the view and needs to perform additional layout to its subviews ("subclasses can override this method as needed"):
public override func layoutSubviews() {
    // We are calling the super method directly here.
    super.layoutSubviews()
    
    // Do more adjustments to this view's subviews...
}So, yes, we can call this method directly when subclassing, but the Swift compiler will not prevent you from do this when not subclassing or from any other foreign class. It will not even issue a warning.
In Objective-C problems like this are usually "solved" my adding a kind of "protected" header (.h) that is intended to be included only when the developer is subclassing. In Swift we do not have headers, but we have the new access level model. So, if the declaration of this method was...
protected func layoutSubviews()... no one outside the class or derived classes would be allowed to call this method directly.
Of course, there are other cases in the Cocoa frameworks and there are many other cases when we are developing software in Swift that the protected access level would be very usefull.
Proposed solution
Create the protected access level.
Detailed design
Reference Types (classes)
When declarated by a class the protected member will be visible to the class itself and all the derived classes.
// BaseClass.swift
public class BaseClass {
    public protected(set) var x = 20
    protected let y = 10
    
    protected func doSomething() {
        // ...
    }
}

// DerivedClass.swift
public class DerivedClass: BaseClass {
    protected override doSomething() {
        self.x = 10 * self.y
    }
}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.
Value Types (structs, enums, etc.)
Value types cannot have derived types. In this case the protected access level does not make sense and will not be allowed in their members.
Protocols
Protocols do not declare access level for their members. So the protected access level is not applicable here.
Extensions
Extensions will not be able do be protected nor their members.
Special Note
The protected access level can only be applied to classes, structs and other types when nested inside other type. So the following code will not compile:
<code style="margin:0px;padding:0px;border:none;border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:3px;border-bottom-left-radius:3px;background-image:none;font-family:Consolas,Monaco,&#39;Andale Mono

[The entire original message is not included.]


(Brent Royal-Gordon) #8

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 :stuck_out_tongue:) 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.

That's a little bit of a different use case than the `layoutSubviews()` case discussed in the proposal.

My answer is this: There is nothing magical about being a subclass that ought to grant access to those methods. For instance, if your subclass grows very complicated and you extract a helper object, it's perfectly reasonable for that helper object to want to access the "subclass-only" API. Contrarily, simple subclasses might not need that API, and exposing it to them would be an unnecessary risk. And there are things which you don't subclass at all which could benefit from being hidden away—think of the Objective-C runtime, which has some parts which every app needs (like the definition of `BOOL`) and other parts which are extraordinarily dangerous and should only be available to code which needs it (like `method_exchangeImplementations`).

The Objective-C solution—using a separate header file—actually acknowledges this fact. Even though the header is called "UIGestureRecognizerSubclass.h", it is not really limited to subclasses; any code can import and use that API. It's just sectioned off *by default*, like keeping all the kitchen knives in a sharps drawer. And the Objective-C runtime, which doesn't contain (many) classes, can use this approach too: <objc/objc.h> is implicitly available, while <objc/runtime.h> is something you have to ask for explicitly.

There are a few ways you could bring this same "sharps drawer" approach to Swift. For instance—without adding any language features—you could create an ancillary struct which merely serves to segregate all the dangerous APIs:

  public class UIGestureRecognizer {
    public private(set) var state: UIGestureRecognizerState {...}
    
    private func ignoreTouch(touch: UITouch, forEvent event: UIEvent) {...}
    private func reset() {...}
    
    // etc. for the other APIs
    
    /// Contains methods and properties which directly affect the state of the gesture recognizer.
    ///
    /// -Warning: Only use the state engine when implementing a custom gesture recognizer yourself.
    /// The state engine is delicate and modifying behind a gesture recognizer's back is likely to
    /// break it.
    public var stateEngine: StateEngine { return StateEngine(gestureRecognizer: self) }
    
    public struct StateEngine {
      private var gestureRecognizer: UIGestureRecognizer
      
      public var state: UIGestureRecognizerState {
        get { return gestureRecognizer.state }
        nonmutating set { gestureRecognizer.state = newValue }
      }
    
      public func ignoreTouch(touch: UITouch, forEvent event: UIEvent) {
        gestureRecognizer.ignoreTouch(touch, forEvent: event)
      }
      
      public func reset() {
        gestureRecognizer.reset()
      }

      /// etc. for the other APIs
    }
  }

Now ordinary clients of UIGestureRecognizer won't see a bunch of random methods strewn around with doc comments warning not to use them; they'll see *one* property with an intimidating name and a scary comment. You could even give the property a name like `internals` to make it clearer that you shouldn't be touching this unless you know what you're doing. On the other hand, any code that needs to *can* access these features, whether or not that code happens to be located in a subclass of the class in question.

Obviously, this approach could benefit from formalization; there are a number of ways that might be done. For instance, you could create a sort of namespace within a class which functions the same way as the `StateEngine` struct and `stateEngine` property in the last example, but without the boilerplate:

  public class UIGestureRecognizer {
    /// Contains methods and properties which directly affect the state of the gesture recognizer.
    ///
    /// -Warning: Only use the state engine when implementing a custom gesture recognizer yourself.
    /// The state engine is delicate and modifying behind a gesture recognizer's back is likely to
    /// break it.
    namespace stateEngine {
      // Note that `self` here is still UIGestureRecognizer.
      
      public var state: UIGestureRecognizerState {...}
      
      public func ignoreTouch(touch: UITouch, forEvent event: UIEvent) {...}
      public func reset() {...}
    }
    
    public var state: UIGestureRecognizerState {
      get { return stateEngine.state }
    }
  }

You could tag particular methods and properties such that files have to ask for access to that subset:

  public class UIGestureRecognizer {
    public(@restricted(set: StateEngine)) var state: UIGestureRecognizerState {...}
    
    public(@restricted(StateEngine)) func ignoreTouch(touch: UITouch, forEvent event: UIEvent) {...}
    public(@restricted(StateEngine)) func reset() {...}
  }

  // In some other file...
  import UIKit
  use UIGestureRecognizer.StateEngine

Or you could move the dangerous members into a submodule and thus require a separate import to see them. I will not speculate on a good submodule syntax, but usage would end up looking like this:

  import UIKit
  import UIKit.UIGestureRecognizerStateEngine

All of these approaches share the virtues of the Objective-C approach:

* Subclasses which don't need the dangerous stuff don't have access to it.
* Code which is not technically a subclass but still requires access *does* have access to it.
* They'll work with types which *don't* get subclassed but similarly have some rare-but-dangerous APIs.

So, just as with controlling override point callers, I think that `protected` is at best a rough approximation of the feature you actually want.

···

--
Brent Royal-Gordon
Architechies


(Charlie Monroe) #9

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 <swift-evolution@swift.org> 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 :stuck_out_tongue:) 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 <swift-evolution@swift.org> 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
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


(Callionica (Swift)) #10

I can't reply directly to all the points in this thread, but I will just
say that there are ways of emulating protected in the language as it exists
today:

If the goal is to have functions that can be overridden by subclasses
without being called by external classes, you can create a parameter of a
public type with a private initializer and use it in all "protected" member
functions. No external code will be able to call such functions because
they cannot create an argument of the correct type.

If external classes need to both override and call the "protected" members,
you'll need to arrange to pass the key to derived classes which you can do
by taking a function as an argument to the base class initializer. The base
class then calls the function to pass the key to the derived class. This
way only parties that are able to control the function passed to the
initializer are able to obtain the key necessary to call the "protected"
member functions.

You can produce a lot of different access control mechanisms using the same
idea.

I'll write something up at http://www.callionica.com/developer in the next
day or so

-- Callionica

···

On Sun, May 29, 2016 at 2:55 PM, Vanderlei Martinelli via swift-evolution < swift-evolution@swift.org> wrote:

Thank you all for your comments. :slight_smile:

Well... My goal is to keep the thing really simple and do not start a new
"OOP x POP" (or "something" x "other thing") war.

"Protected" access level is not a new concept at all (except for the Swift
language), so I did not propose anything preposterous.

Of course in the Swift of my dreams we also have "abstract" access level
modifier, "protected" access level, *real* "private" access level and
"file" access level modifier (along with many, many other things, of
course). But this proposal is not about this. It is only about include the
"protected" access level.

There is, however, something that I need to get off my chest: I really
would like to have the freedom to go to the depths with protocols as well
with classes. I work in real apps everyday that uses Cocoa frameworks
(based on classes) and these apps must be shipped and I like them well
written. Maybe am I insane for proposing a better support for classes in
Swift? If so, this explains why every time I suggest better support for
classes in Swift there is an endless discussion and someone proclaims the
death of OOP and is it. OK... Maybe someday we will not have more classes
in Swift. Until there: the current language status is the best way to
handle OOP in Swift? Or is there a better way? I think there is.

Regards,

Vanderlei Martinelli

On Sat, May 28, 2016 at 7:52 PM, Vanderlei Martinelli < > vmartinelli@alecrim.com> wrote:

Hello.

This is the first draft. I'd like to know your opinion about it.

(I know that this subject could have been discussed before. If so, please
indicate me the correct thread to follow and interact.)

Regards,

Vanderlei Martinelli

---

Introduction

Protected access level will enable entities to be used within the
container type and by derived types only.
Motivation

Today Swift has three access levels (public, internal and private), but
lacks a way to describe a member that can be only visible to its type or
derived types.

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."

But yes, you should call this method directly if you are subclassing the
view and needs to perform additional layout to its subviews ("subclasses
can override this method as needed"):

public override func layoutSubviews() {
    // We are calling the super method directly here.
    super.layoutSubviews()

    // Do more adjustments to this view's subviews...}

So, yes, we can call this method directly when subclassing, but the Swift
compiler will not prevent you from do this when not subclassing or from any
other foreign class. It will not even issue a warning.

In Objective-C problems like this are usually "solved" my adding a kind
of "protected" header (.h) that is intended to be included only when the
developer is subclassing. In Swift we do not have headers, but we have the
new access level model. So, if the declaration of this method was...

protected func layoutSubviews()

... no one outside the class or derived classes would be allowed to call
this method directly.

Of course, there are other cases in the Cocoa frameworks and there are
many other cases when we are developing software in Swift that the
protected access level would be very usefull.
Proposed solution

Create the protected access level.
Detailed designReference Types (classes)

When declarated by a class the protected member will be visible to the
class itself and all the derived classes.

// BaseClass.swiftpublic class BaseClass {
    public protected(set) var x = 20
    protected let y = 10

    protected func doSomething() {
        // ...
    }}
// DerivedClass.swiftpublic class DerivedClass: BaseClass {
    protected override doSomething() {
        self.x = 10 * self.y
    }}

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.
Value Types (structs, enums, etc.)

Value types cannot have derived types. In this case the protected access
level does not make sense and will not be allowed in their members.
Protocols

Protocols do not declare access level for their members. So the protected access
level is not applicable here.
Extensions

Extensions will not be able do be protected nor their members.
Special Note

The protected access level can only be applied to classes, structs and
other types when nested inside other type. So the following code will not
compile:

// ERROR: A first level class cannot be protected.
protected class MyProtectedClass {
    /// ...}

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 {
        /// ...
    }}
// Since `MyProtectedClass` is nested and protected we// can have access to it here.public class MyDerivedClass: MyPublicClass {
    public func doSomething() {
        let c = MyProtectedClass()

        /// ...
    }}

Impact on existing code

The imported Cocoa frameworks may have annotations on the "Objective-C
side" that will inform if one member is protected. If this will be case
(for the layoutSubviews method, as example), only code accessing these
now protected members outside the owner class and its derived classes will
have to change (currently this can be considered an error, anyway).

Any other code will be not impacted by this feature as it is new to the
language.
Alternatives considered

Do not add the protected access level to the current model.

---

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


(Callionica (Swift)) #11

Here's demo code for emulating protected in Swift:
https://gist.github.com/callionica/19da0763c0b0b4c504fc5f46d07e8ee8


(Leonardo Pessoa) #12

Vanderlei, my point in bringing such topics to this discussion is to make everyone here think if we're trying to really enhance the language within its intended purpose or if we're trying to change the language into something else were familiar with from other languages we work/ed with just because we're used to work like that. I just started thinking about this today and just cannot stop now. No intention to start a war here but I think everyone should ask themselves this for every proposed change to the language.

About the topic at-hand, we have to remember Swift is bridged to Objective-C, which has no protected (or abstract). How do you propose these protected members be bridged should the proposal pass?

···

-----Original Message-----
From: "Vanderlei Martinelli via swift-evolution" <swift-evolution@swift.org>
Sent: ‎29/‎05/‎2016 06:56 PM
To: "swift-evolution" <swift-evolution@swift.org>
Subject: Re: [swift-evolution] [Proposal] Protected Access Level

Thank you all for your comments. :slight_smile:

Well... My goal is to keep the thing really simple and do not start a new "OOP x POP" (or "something" x "other thing") war.

"Protected" access level is not a new concept at all (except for the Swift language), so I did not propose anything preposterous.

Of course in the Swift of my dreams we also have "abstract" access level modifier, "protected" access level, *real* "private" access level and "file" access level modifier (along with many, many other things, of course). But this proposal is not about this. It is only about include the "protected" access level.

There is, however, something that I need to get off my chest: I really would like to have the freedom to go to the depths with protocols as well with classes. I work in real apps everyday that uses Cocoa frameworks (based on classes) and these apps must be shipped and I like them well written. Maybe am I insane for proposing a better support for classes in Swift? If so, this explains why every time I suggest better support for classes in Swift there is an endless discussion and someone proclaims the death of OOP and is it. OK... Maybe someday we will not have more classes in Swift. Until there: the current language status is the best way to handle OOP in Swift? Or is there a better way? I think there is.

Regards,

Vanderlei Martinelli

On Sat, May 28, 2016 at 7:52 PM, Vanderlei Martinelli <vmartinelli@alecrim.com> wrote:

Hello.

This is the first draft. I'd like to know your opinion about it.

(I know that this subject could have been discussed before. If so, please indicate me the correct thread to follow and interact.)

Regards,

Vanderlei Martinelli

---

Introduction
Protected access level will enable entities to be used within the container type and by derived types only.
Motivation
Today Swift has three access levels (public, internal and private), but lacks a way to describe a member that can be only visible to its type or derived types.
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."
But yes, you should call this method directly if you are subclassing the view and needs to perform additional layout to its subviews ("subclasses can override this method as needed"):
public override func layoutSubviews() {
    // We are calling the super method directly here.
    super.layoutSubviews()
    
    // Do more adjustments to this view's subviews...
}So, yes, we can call this method directly when subclassing, but the Swift compiler will not prevent you from do this when not subclassing or from any other foreign class. It will not even issue a warning.
In Objective-C problems like this are usually "solved" my adding a kind of "protected" header (.h) that is intended to be included only when the developer is subclassing. In Swift we do not have headers, but we have the new access level model. So, if the declaration of this method was...
protected func layoutSubviews()... no one outside the class or derived classes would be allowed to call this method directly.
Of course, there are other cases in the Cocoa frameworks and there are many other cases when we are developing software in Swift that the protected access level would be very usefull.
Proposed solution
Create the protected access level.
Detailed design
Reference Types (classes)
When declarated by a class the protected member will be visible to the class itself and all the derived classes.
// BaseClass.swift
public class BaseClass {
    public protected(set) var x = 20
    protected let y = 10
    
    protected func doSomething() {
        // ...
    }
}

// DerivedClass.swift
public class DerivedClass: BaseClass {
    protected override doSomething() {
        self.x = 10 * self.y
    }
}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.
Value Types (structs, enums, etc.)
Value types cannot have derived types. In this case the protected access level does not make sense and will not be allowed in their members.
Protocols
Protocols do not declare access level for their members. So the protected�

[The entire original message is not included.]


(Tino) #13

I can't reply directly to all the points in this thread, but I will just say that there are ways of emulating protected in the language as it exists today:

If the goal is to have functions that can be overridden by subclasses without being called by external classes, you can create a parameter of a public type with a private initializer and use it in all "protected" member functions. No external code will be able to call such functions because they cannot create an argument of the correct type.

As others stated before:
I agree it's a cool little hack, but a hack none the less — and imho it isn't useful in real life.
The focus should be documenting intend ("this method should be used in a certain way"). Creating clever countermeasures to stop users of a library to enforce limitations is imho the wrong way.
<not serious>
We could even use tools like public key cryptography to secure access to APIs — or is there any developer who doesn't want to deal with more certificates? :wink:
</not serious>


(Goffredo Marocchi) #14

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 <swift-evolution@swift.org> 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 <swift-evolution@swift.org> 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 :stuck_out_tongue:) 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 <swift-evolution@swift.org> 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
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

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


(Goffredo Marocchi) #15

There is nothing magical about being a subclass that ought to grant access to those methods

Hello Brent,

You must admit that when thinking about the words inheritance and parent/child classes the mind wanders to suggest the contrary actually. I know it is a personal loaded opinion, but I think having private headers to expose properties, variables, and methods to subclasses as leading to poor looking code generally (one must also keep in mind teams composed of less than pure elite coders) and in itself I have never liked the concept.

Subclassing with abstract methods and protected access always felt natural and I think we should give that power to those who use Swift rather than tie OOP's arms a bit behind its back. It would be wrong if this were to happen in order to push people down to functional or POP approaches just because subclassing can be abused.
I have always felt that we should give better options rather than restricting what we feel it may be abused (yet useful if used correctly).

···

Sent from my iPhone

On 29 May 2016, at 11:30, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:


(Charlie Monroe) #16

My answer is this: There is nothing magical about being a subclass that ought to grant access to those methods.

There is - it's a family member that you let use your summer house. Without the metaphor, here is an example for this being useful:

URLConnection subclass may want to update the URL in case of a redirect, but you don't really want to expose the setter for the URL to public.

For instance, if your subclass grows very complicated and you extract a helper object, it's perfectly reasonable for that helper object to want to access the "subclass-only" API.

That's what "friend" classes are for in C++, similar concept would be applied here.

···

Contrarily, simple subclasses might not need that API, and exposing it to them would be an unnecessary risk. And there are things which you don't subclass at all which could benefit from being hidden away—think of the Objective-C runtime, which has some parts which every app needs (like the definition of `BOOL`) and othe


(L Mihalkovic) #17

let’s perpetuate the helplessness because developers will not understand. That might be the rationale that also prevailed to make swift have no namespaces: Objective-c did not… funny thing is, the compiler source code would be miserable to read if its source code was organized in terms of classes and modules (lib). Which means that aside from accessibility to LLVM API, attempting to write a Swift compiler in Swift today would probably result in hard to read/reason about source code.

···

On May 29, 2016, at 9:38 AM, Charlie Monroe via swift-evolution <swift-evolution@swift.org> 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


(Vanderlei Martinelli) #18

Hi Leonardo.

Thank you for your answer.

I understand your point of view and I agree that it is better to look
forward. But today we still have to deal with decades of legacy Cocoa code
written using classes. If tomorrow a new set of Cocoa frameworks written in
Swift using protocols appears, maybe we can forget all of this discussion.
But OOP is very important and don't think it is the "past". I see future in
"POP" as I see in "OOP" and I think we can have OOP and POP living in
harmony in Swift. And since we have support to classes in Swift, I think it
shall have a full featured support for classes.

Perhaps my reaction in the last message sounds like I am overreacting when
seen in the context of this thread. But I am programming in Swift since the
first day it was publicly available (and I think that the first almost
usable version to create real world apps was the 1.2). Since the old
forums, when Swift was not yet open source, I have been insisting on
certain improvements.

About bridging member declarations from Objective-C, many of these classes
already have separated headers with the members intended to be overrided.
Exceptions to this rule could be "annotated" somehow. (I would like to
mention classes that are entirely intended to be subclassed and not used as
is in Cocoa frameworks, but this is about "abstract" access level modifier
and not part of this proposal.)

Regards,

Vanderlei Martinelli

···

On Sun, May 29, 2016 at 7:45 PM, Leonardo Pessoa <me@lmpessoa.com> wrote:

Vanderlei, my point in bringing such topics to this discussion is to make
everyone here think if we're trying to really enhance the language within
its intended purpose or if we're trying to change the language into
something else were familiar with from other languages we work/ed with just
because we're used to work like that. I just started thinking about this
today and just cannot stop now. No intention to start a war here but I
think everyone should ask themselves this for every proposed change to the
language.

About the topic at-hand, we have to remember Swift is bridged to
Objective-C, which has no protected (or abstract). How do you propose these
protected members be bridged should the proposal pass?
------------------------------
From: Vanderlei Martinelli via swift-evolution <swift-evolution@swift.org>
Sent: ‎29/‎05/‎2016 06:56 PM
To: swift-evolution <swift-evolution@swift.org>
Subject: Re: [swift-evolution] [Proposal] Protected Access Level

Thank you all for your comments. :slight_smile:

Well... My goal is to keep the thing really simple and do not start a new
"OOP x POP" (or "something" x "other thing") war.

"Protected" access level is not a new concept at all (except for the Swift
language), so I did not propose anything preposterous.

Of course in the Swift of my dreams we also have "abstract" access level
modifier, "protected" access level, *real* "private" access level and
"file" access level modifier (along with many, many other things, of
course). But this proposal is not about this. It is only about include the
"protected" access level.

There is, however, something that I need to get off my chest: I really
would like to have the freedom to go to the depths with protocols as well
with classes. I work in real apps everyday that uses Cocoa frameworks
(based on classes) and these apps must be shipped and I like them well
written. Maybe am I insane for proposing a better support for classes in
Swift? If so, this explains why every time I suggest better support for
classes in Swift there is an endless discussion and someone proclaims the
death of OOP and is it. OK... Maybe someday we will not have more classes
in Swift. Until there: the current language status is the best way to
handle OOP in Swift? Or is there a better way? I think there is.

Regards,

Vanderlei Martinelli

On Sat, May 28, 2016 at 7:52 PM, Vanderlei Martinelli < > vmartinelli@alecrim.com> wrote:

Hello.

This is the first draft. I'd like to know your opinion about it.

(I know that this subject could have been discussed before. If so, please
indicate me the correct thread to follow and interact.)

Regards,

Vanderlei Martinelli

---

Introduction

Protected access level will enable entities to be used within the
container type and by derived types only.
Motivation

Today Swift has three access levels (public, internal and private), but
lacks a way to describe a member that can be only visible to its type or
derived types.

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."

But yes, you should call this method directly if you are subclassing the
view and needs to perform additional layout to its subviews ("subclasses
can override this method as needed"):

public override func layoutSubviews() {
    // We are calling the super method directly here.
    super.layoutSubviews()

    // Do more adjustments to this view's subviews...}

So, yes, we can call this method directly when subclassing, but the Swift
compiler will not prevent you from do this when not subclassing or from any
other foreign class. It will not even issue a warning.

In Objective-C problems like this are usually "solved" my adding a kind
of "protected" header (.h) that is intended to be included only when the
developer is subclassing. In Swift we do not have headers, but we have the
new access level model. So, if the declaration of this method was...

protected func layoutSubviews()

... no one outside the class or derived classes would be allowed to call
this method directly.

Of course, there are other cases in the Cocoa frameworks and there are
many other cases when we are developing software in Swift that the
protected access level would be very usefull.
Proposed solution

Create the protected access level.
Detailed designReference Types (classes)

When declarated by a class the protected member will be visible to the
class itself and all the derived classes.

// BaseClass.swiftpublic class BaseClass {
    public protected(set) var x = 20
    protected let y = 10

    protected func doSomething() {
        // ...
    }}
// DerivedClass.swiftpublic class DerivedClass: BaseClass {
    protected override doSomething() {
        self.x = 10 * self.y
    }}

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.
Value Types (structs, enums, etc.)

Value types cannot have derived types. In this case the protected access
level does not make sense and will not be allowed in their members.
Protocols

Protocols do not declare access level for their members. So the protected

[The entire original message is not included.]


(Rod Brown) #19

Brent,

I personally was also thinking of the UIGestureRecognizerSubclass example, and I think it’s the best example of ‘protected’ use case, but I think I came to a slightly different position you did.

You come to this from the perspective of “can we do something with the same limitations of UIGestureRecognizerSubclass”? I come at this from the exact opposite view, I think UIGestureRecognizer shows the problem with the Obj-C solution. It’s a workaround for a painful limitation of Obj-C. This is something we should be looking to provide a better solution for, not “as good as”. I also think your solution is quite convoluted simply to avoid a “protected” solution.

I really liked your idea for “private(call)” but I think that the UIGestureRecognizer example still could not be implemented without a “protected(call)”. Internal state needs to be updated by the gesture recognizer subclass, outside the initial module (UIKit), but these setters and accessors should never be called externally of a subclass. That, in my mind, is the definition of “protected”.

You seem to be producing a convoluted solution to create a “sharps drawer” but Swift is supposed to be safe. You shouldn’t access the sharps drawer there and there are better ways for us to stop you than to simply “trust” the developer - it’s to do the right thing, and provide a protection level that stops the access where it’s not needed: “protected”.

Protected would not fix some of the issues with calling methods like “layoutSubviews()” but we can address them otherwise as they’re not really in the scope “protected” addresses.

- Rod

···

On 29 May 2016, at 8:30 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> 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 :stuck_out_tongue:) 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.

That's a little bit of a different use case than the `layoutSubviews()` case discussed in the proposal.

My answer is this: There is nothing magical about being a subclass that ought to grant access to those methods. For instance, if your subclass grows very complicated and you extract a helper object, it's perfectly reasonable for that helper object to want to access the "subclass-only" API. Contrarily, simple subclasses might not need that API, and exposing it to them would be an unnecessary risk. And there are things which you don't subclass at all which could benefit from being hidden away—think of the Objective-C runtime, which has some parts which every app needs (like the definition of `BOOL`) and other parts which are extraordinarily dangerous and should only be available to code which needs it (like `method_exchangeImplementations`).

The Objective-C solution—using a separate header file—actually acknowledges this fact. Even though the header is called "UIGestureRecognizerSubclass.h", it is not really limited to subclasses; any code can import and use that API. It's just sectioned off *by default*, like keeping all the kitchen knives in a sharps drawer. And the Objective-C runtime, which doesn't contain (many) classes, can use this approach too: <objc/objc.h> is implicitly available, while <objc/runtime.h> is something you have to ask for explicitly.

There are a few ways you could bring this same "sharps drawer" approach to Swift. For instance—without adding any language features—you could create an ancillary struct which merely serves to segregate all the dangerous APIs:

  public class UIGestureRecognizer {
    public private(set) var state: UIGestureRecognizerState {...}
    
    private func ignoreTouch(touch: UITouch, forEvent event: UIEvent) {...}
    private func reset() {...}
    
    // etc. for the other APIs
    
    /// Contains methods and properties which directly affect the state of the gesture recognizer.
    ///
    /// -Warning: Only use the state engine when implementing a custom gesture recognizer yourself.
    /// The state engine is delicate and modifying behind a gesture recognizer's back is likely to
    /// break it.
    public var stateEngine: StateEngine { return StateEngine(gestureRecognizer: self) }
    
    public struct StateEngine {
      private var gestureRecognizer: UIGestureRecognizer
      
      public var state: UIGestureRecognizerState {
        get { return gestureRecognizer.state }
        nonmutating set { gestureRecognizer.state = newValue }
      }
    
      public func ignoreTouch(touch: UITouch, forEvent event: UIEvent) {
        gestureRecognizer.ignoreTouch(touch, forEvent: event)
      }
      
      public func reset() {
        gestureRecognizer.reset()
      }

      /// etc. for the other APIs
    }
  }

Now ordinary clients of UIGestureRecognizer won't see a bunch of random methods strewn around with doc comments warning not to use them; they'll see *one* property with an intimidating name and a scary comment. You could even give the property a name like `internals` to make it clearer that you shouldn't be touching this unless you know what you're doing. On the other hand, any code that needs to *can* access these features, whether or not that code happens to be located in a subclass of the class in question.

Obviously, this approach could benefit from formalization; there are a number of ways that might be done. For instance, you could create a sort of namespace within a class which functions the same way as the `StateEngine` struct and `stateEngine` property in the last example, but without the boilerplate:

  public class UIGestureRecognizer {
    /// Contains methods and properties which directly affect the state of the gesture recognizer.
    ///
    /// -Warning: Only use the state engine when implementing a custom gesture recognizer yourself.
    /// The state engine is delicate and modifying behind a gesture recognizer's back is likely to
    /// break it.
    namespace stateEngine {
      // Note that `self` here is still UIGestureRecognizer.
      
      public var state: UIGestureRecognizerState {...}
      
      public func ignoreTouch(touch: UITouch, forEvent event: UIEvent) {...}
      public func reset() {...}
    }
    
    public var state: UIGestureRecognizerState {
      get { return stateEngine.state }
    }
  }

You could tag particular methods and properties such that files have to ask for access to that subset:

  public class UIGestureRecognizer {
    public(@restricted(set: StateEngine)) var state: UIGestureRecognizerState {...}
    
    public(@restricted(StateEngine)) func ignoreTouch(touch: UITouch, forEvent event: UIEvent) {...}
    public(@restricted(StateEngine)) func reset() {...}
  }

  // In some other file...
  import UIKit
  use UIGestureRecognizer.StateEngine

Or you could move the dangerous members into a submodule and thus require a separate import to see them. I will not speculate on a good submodule syntax, but usage would end up looking like this:

  import UIKit
  import UIKit.UIGestureRecognizerStateEngine

All of these approaches share the virtues of the Objective-C approach:

* Subclasses which don't need the dangerous stuff don't have access to it.
* Code which is not technically a subclass but still requires access *does* have access to it.
* They'll work with types which *don't* get subclassed but similarly have some rare-but-dangerous APIs.

So, just as with controlling override point callers, I think that `protected` is at best a rough approximation of the feature you actually want.

--
Brent Royal-Gordon
Architechies

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


(Charles Srstka) #20

Couldn’t you use the same reasoning to claim there shouldn’t be access control at all? Just add documention on methods you want to mark private telling users of the library not to call them!

Charles

···

On May 30, 2016, at 3:42 AM, Tino Heth via swift-evolution <swift-evolution@swift.org> wrote:

I can't reply directly to all the points in this thread, but I will just say that there are ways of emulating protected in the language as it exists today:

If the goal is to have functions that can be overridden by subclasses without being called by external classes, you can create a parameter of a public type with a private initializer and use it in all "protected" member functions. No external code will be able to call such functions because they cannot create an argument of the correct type.

As others stated before:
I agree it's a cool little hack, but a hack none the less — and imho it isn't useful in real life.
The focus should be documenting intend ("this method should be used in a certain way"). Creating clever countermeasures to stop users of a library to enforce limitations is imho the wrong way.