Allow subclass to be more visible than its superclass

I propose that having two classes Fruit and Apple, Apple being subclass of Fruit and Fruit being an internal class, Apple should be allowed to be declared public or even open. Similarly, private class should be extendable by internal, public or open class.

Note: By visibility I mean what Swift documentation addresses as Access Levels.

Theoretical rationale
From conceptual point of view, I believe there is nothing against that and other languages allows it. Inheritance is generalization/specialization relationship: Everything we can do with a class we should also be able to do with its subclasses but that is not true vice-versa.Therefore, it conceptually makes more sense to require subclasses of a class accessible from other modules to be also accessible from other modules (and Swift allows to limit them to current module) than the other way around (which is currently prohibited).

Practical rationale
It is common design to create common superclass to remove code duplication and implement common functionality. It might be desirable to expose derived subclasses but not their common ancestor. For example, I am implementing an iOS library with a generic class that has several non-generic subclasses. Only the subclasses are intended to be used by clients of the library not the original class. It is undesirable to allow custom subclasses in client modules but that in current state it cannot be prevented. I can only replace inheritance by composition and that would mean a lot of unnecessary boilerplate code. As another example, it is imaginable that UIKIt would only want to expose subclass of UIView but not the vanilla UIView.

Current state
Compiler gives error “Class cannot be declared public because its superclass is internal”.

Swift language consistancy
It would also make Swift a visibility feel more consistent internally as extending class members visibility is already possible in Swift. E.g. you can override internal property in a subclass and declare it open or public. Why would you be prevented from doing the same with the subclass itself?

With extending subclass members visibility it is already possible to simulate extending subclass visibility. E.g. It is possible to create open class with all members internal and then inherit from it and override all members to be open. While this leads to the same result from perspective of what can be done with the code, it requires lots of unnecessary code and exposes extra and very strange "empty class" to client modules (it is the superclass with all members internal).

2 Likes

May be interesting: Should not be public because its superclass is internal

I read the discussion but while it is somewhat related it is not the same discussion. Here, I am dealing with inheriting from class of more limited visibility while discussion linked is about using class of more limited visibility as a generic parameter.

@jrose, does your explanation in the linked thread apply to the general case of a more accessible inheritance from something less accessible?

I do not think it does at least not that particular explanation. The reason is that the complier already checks today whether all types used in let's say public member a are at least public. If that is kept in my case, overriding something internal and increasing accessibility to public would also require check if associated types are also public.

There's at least one statement from core: Public classes with private superclass
It's quite old, but I think the situation didn't change much.

Another aspect that's imho relevant for the proposal is that we don't have abstract classes, and the most common workaround are methods that fail on runtime.
It would at least help in some situations hide the problematic parts, and still expose subclasses which implement all behavior.

Type visibility is quite different from member visibility. It is also important to understand this concept is different from inheriting with a certain access level, like it can be done in cpp. The idea seems cautiously optimistic and I'm sure it's not the first time someone brings it up. IIRC, @Chris_Lattner3 once expressed his approval for this in an old thread. I must admit though I had an urge for this kind of feature a couple of times.

One of the things worth mentioning are redeclarations in extensions which should be okay from the consumer's side but apparently become an ambiguity from the opposite side. Also, this will impose the feature on protocols as well, which might be unexpected.

Tino, the linked discussion is just great! I am very happy that somebody opened the topic before. I basically agree with everything you wrote there. I also think that abstract classes would be stronger tool to have... But they seems to be ton of work and hiding a class in your hierarchy should not be so hard. So I would be happy to have just this why we are waiting for abstract classes.

There is no statement why this might be a bad thing in the linked discussion. Opinions vary on usefullness (some calling it "aestetics") but I believe that for framework authors this would be helpfull. And also, I believe this is an artificial restriction that should be removed for the language to make more sense.

I undertand that. However, I think that if anything can break this it is generics again but of different kind that @jrose used to in discussion you linked. Here is an example of what might be problematic:

internal protocol Car {}
internal class Garage<T: Car> {}
public class SpecialGarage<T: Car>: Garage<T> {} //this should be an error because Car is not public

Cool thing is that Swift already catches this with error Generic class cannot be declared public because its generic parameter uses an internal type. In the other hand soemthing like this should be allowed:

internal protocol Vehicle {}
internal class Garage<T: Vehicle> {}
public class Car: Vehicle {}
public class SpecialGarage<T: Car>: Garage<T> {} //no problem

Be aware that afaics it's not unlikely that Swift will never have abstract classes (sometimes, I get the feeling we are lucky to have classes and inheritance at all ;-).
But I think the pitch is useful no matter what else will change in Swift - and it's not really a new feature, but rather removal of an restriction, so it adds no complexity for users of the language (it might be a different story for writers of the compiler, though ;-)

I'm definitely pretty against the idea of internal superclass, public subclass myself. What do you show to clients? What if the internal superclass itself has a public superclass? But as John said on that old thread, it's probably doable if someone designs it. I just think it makes the language more complicated without enough of a benefit.

(Note that I'm not saying there's no benefit, only that the benefit that there is doesn't justify the increased complexity for me.)

And FWIW, while abstract classes as such don't interest me at all, a way to say "you must override this method/property" does seem useful. You could even provide a default implementation, and allow users to call super; the point is that it makes it a choice. That seems like the useful feature in that space. But that's different from "this superclass is an implementation detail", the original topic.

2 Likes