Now that the scoped access discussion is close(r) to being settled, let's 
tackle the next controversial topic: protected.


Expose

Objective-C allows multiple “APIs” to be exposed for a given class by virtue of 
writing additional header files. There are two common uses of that:

Use case 1: API for private clients

Foo.h
Foo-Private.h
Foo.m

Use case 2: API for subclasses, intended to be overridden

Foo.h
Foo-Subclasses.h  // defines a method to override, e.g. layoutSubviews
Foo.m

Use case 3: API for subclasses, intended to be called

Foo.h
Foo-Subclasses.h  // defines a dangerous helper method
Foo.m

Swift covers use case #1 with the internal (or perhaps soon-to-be 
moduleprivate) access modifier.


The problem

Swift core teams has prompted us to explore using internal and file-private 
access levels to cover use cases #2 and #3, and I've faithfully tried to do 
just that. It works for many cases inside the code of apps, but it doesn't work 
if you're writing a library package and want to expose the “special extenders 
club” API to the clients of the library.

Here's the use case in more detail:

1. You have a library that exposes a class called Foo.
2. There are two different ways to use that library: you can just use Foo, or 
you can extend the library, perhaps subclassing Foo.
3. Those who opt for the second way to use the library (i.e. extending it) need 
access to a broader set of APIs.


Why do extenders need access to a broader set of APIs?

Basically, for safety and documentation purposes.

Considering use case #2:

The library often exposes very similar APIs, some of which are intended to be 
called (layoutIfNeeded), others intended to be extended (layoutSubviews). These 
typically sound very similar, and are easy to mix up, so you definitely want to 
document this difference, and ideally you also want to statically prevent the 
first (non-extending) type of clients from calling them.

Note that accidentally calling layoutSubviews instead of layoutIfNeeded is one 
of the worst kinds of mistakes; it might seem to work, and it might not be 
found until production, and the bug it causes may be weird and difficult to 
reproduce.


Considering use case #3:

A library may expose methods that should not be called by normal clients, but 
are useful when extending it:

3.1) They may carry substantially different guarantees from normal methods 
(e.g. require to be called only on a certain dispatch queue),

3.2) or may only be callable while another overridable method is executing,

3.3) or may simply allow mutating the internal state of the class (e.g. the 
state of UIGestureRecognizer) that is not supposed to be mutated from outside.


Swift currently requires making all of this public. :-(


The problem has tons of prior art.

C family of languages use multiple header files.

Ada has special access rules for subpackages (nicely explained in 
http://blog.spacesocket.com/2012/07/31/ada-child-packages/).

OOP languages have protected access levels.


What does extending mean in Swift?

I'm not sure about this. It certainly needs to include subclassing a class, but 
we may find other useful meanings.

In particular, writing an extension for a struct/class/protocol may raise 
similar concerns and may need similar treatment.


What do I propose?

I'm mainly looking for ideas and discussion, but as a strawman, let me put this 
out:

Introduce protected access modifier that allows the member to be accessed by:

1. Subclasses of a class.
2. Extenders of a struct or protocol.
3. Implementors of a protocol. 

package FooKit:

public class Foo {
        public func amSafe() { ... }
        protected func amDangerous() { ... }
}

public struct Boo {
        public func amSafe() { ... }
        protected func amDangerous() { ... }
}

public protocol Moo {
        func amSafe()
}

public extension Moo {
        protected func amDangerous() { ... }
}

App:

        public class Bar: Foo {
                public func bar() {
                        amDangerous()
                }
        }

        public extension Foo {
                public func boz() {
                        amDangerous()
                }
        }

        public extension Boo {
                public func boz() {
                        amDangerous()
                }
        }

        public class Boz: Moo {
                public func fubar() {
                        amDangerous()
                }
        }


So what do you think?

A.

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to