Public classes with private superclass


(Tino) #1

I'm running into "class cannot be declared public because its superclass is internal" issues on a regular basis, and I wonder if it wouldn't make sense to allow this combination:
It might be less useful as soon as there are abstract classes or generic protocols, but even then I think I'd like to have this "feature".

Inheritance can be such a private thing ;-), so imho there should be an obvious way to hide it (marking all init-methods internal works, but I don't think this is a good way to express the intention).

Tino


Allow subclass to be more visible than its superclass
(Andre) #2

Personally, it's perhaps more of an "aesthetic" thing I suppose but I would definitely prefer that my internal classes stay internal even if there is a public subclass... I don't like how that leaks my internal implementation like that...

Though maybe the more swift thing to do would be to use private extensions in place of private superclasses I suppose...

andre

iPhoneから送信

2016/07/04 21:31、Tino Heth via swift-evolution <swift-evolution@swift.org> のメッセージ:

···

I'm running into "class cannot be declared public because its superclass is internal" issues on a regular basis, and I wonder if it wouldn't make sense to allow this combination:
It might be less useful as soon as there are abstract classes or generic protocols, but even then I think I'd like to have this "feature".

Inheritance can be such a private thing ;-), so imho there should be an obvious way to hide it (marking all init-methods internal works, but I don't think this is a good way to express the intention).

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


(Charlie Monroe) #3

How would this look in the exported API? Would it simply show itself as not inheriting from anything? If your base class inherited from something (e.g. NSObject), would the public subclasses show themselves as direct subclasses of that superclass (NSObject)?

IMHO these things can be solved by private protocols with default implementation to reuse the code:

internal protocol MyClassCore { }
extension MyClassCore {
  func doSomething() {
    print("123")
  }
}

public class MyClass1 { }
extension MyClass1: MyClassCore { }

Or you can just create a private class and use its instance in your classes:

internal class MyClassImplementation {
  /// Implement shared code
}

public class MyClass {
  private var _impl = MyClassImplementation()
}

Which is fairly common solution.

Anyway, this is definitely out of scope of Swift 3 and as has been pointed out several times in the past month here, all discussions here should now focus on Swift 3 features.

···

On Jul 5, 2016, at 8:49 AM, Andre via swift-evolution <swift-evolution@swift.org> wrote:

Personally, it's perhaps more of an "aesthetic" thing I suppose but I would definitely prefer that my internal classes stay internal even if there is a public subclass... I don't like how that leaks my internal implementation like that...

Though maybe the more swift thing to do would be to use private extensions in place of private superclasses I suppose...

andre

iPhoneから送信

2016/07/04 21:31、Tino Heth via swift-evolution <swift-evolution@swift.org> のメッセージ:

I'm running into "class cannot be declared public because its superclass is internal" issues on a regular basis, and I wonder if it wouldn't make sense to allow this combination:
It might be less useful as soon as there are abstract classes or generic protocols, but even then I think I'd like to have this "feature".

Inheritance can be such a private thing ;-), so imho there should be an obvious way to hide it (marking all init-methods internal works, but I don't think this is a good way to express the intention).

Tino
_______________________________________________
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


(Haravikk) #4

The other alternative is to just declare the methods of the "private" class private/internal and then use them. For example:

  public class Foo {
    internal init() { … } // Can't be instantiated outside of this module
    internal func doSomething() { … }
    func publicMethod() { … }
  }

  public class Bar : Foo {
    public init() { super.init() } // Can be instantiated outside of this module
    public something() { super.doSomething() }
  }

But yeah, it seems like there are a lot of design patterns to solve this problem already; it's a nice idea, but proper abstract classes or traits/mixins are better possible solutions in the long-run, and the use of internal initialisers effectively result in abstract internal types for the time being.

···

On 5 Jul 2016, at 08:01, Charlie Monroe via swift-evolution <swift-evolution@swift.org> wrote:

IMHO these things can be solved by private protocols with default implementation to reuse the code:
Or you can just create a private class and use its instance in your classes:


(Tino) #5

How would this look in the exported API? Would it simply show itself as not inheriting from anything? If your base class inherited from something (e.g. NSObject), would the public subclasses show themselves as direct subclasses of that superclass (NSObject)?

I wouldn't care, but the last option sounds sensible (although I hope I'll never have to directly subclass NSObject in the future :wink:

IMHO these things can be solved by private protocols with default implementation to reuse the code:

Not everything can be solved with protocols (stored properties, anyone?)

Or you can just create a private class and use its instance in your classes:

internal class MyClassImplementation {
  /// Implement shared code
}

public class MyClass {
  private var _impl = MyClassImplementation()
}
Which is fairly common solution.

Those six lines are written easily, but they don't do anything, so you have to add tons of boilerplate to do forwarding — and you destroy the internal class hierarchy.
Do you see a problem with "internal init"?

Anyway, this is definitely out of scope of Swift 3 and as has been pointed out several times in the past month here, all discussions here should now focus on Swift 3 features.

Sorry, I've been busy with other things lately…
I know that anything that breaks compatibility with existing source has top priority now, and that there most likely won't be any resources to integrate stuff that doesn't fulfill that criterion — but has there been an actual announcement that no discussions about purely additive changes should be started at all?

Best regards,
Tino


(Charlie Monroe) #6

How would this look in the exported API? Would it simply show itself as not inheriting from anything? If your base class inherited from something (e.g. NSObject), would the public subclasses show themselves as direct subclasses of that superclass (NSObject)?

I wouldn't care, but the last option sounds sensible (although I hope I'll never have to directly subclass NSObject in the future :wink:

NSObject was just an example, of course, you can base it on NSView, etc.

IMHO these things can be solved by private protocols with default implementation to reuse the code:

Not everything can be solved with protocols (stored properties, anyone?)

Sure, it's not completely painless, but you can declare

internal protocol MyClassProtocol {
  var foo: Bar { get set }
}

public class MyClass: MyClassProtocol {
  public var foo: Bar
}

It's 1 extra line of code. You declare the contract on stored properties by the protocol and just declare them on the class.

Or you can just create a private class and use its instance in your classes:

internal class MyClassImplementation {
  /// Implement shared code
}

public class MyClass {
  private var _impl = MyClassImplementation()
}
Which is fairly common solution.

Those six lines are written easily, but they don't do anything, so you have to add tons of boilerplate to do forwarding — and you destroy the internal class hierarchy.

Yes, but I see a ton of various advantages such as breaking the code into various classes dealing with a particular problem. It seems to me like if you really need to hide the hierarchy, you should look into class clusters - such as NSString, where you declare a public abstract API and return internal subclasses using factory methods (yes, it is the exact opposite of what you want, but depending on your exact problem, it may get solved by this).

IMHO if you need to hide the hierarchy *that* bad, it usually points to a bad design.

I know that anything that breaks compatibility with existing source has top priority now, and that there most likely won't be any resources to integrate stuff that doesn't fulfill that criterion — but has there been an actual announcement that no discussions about purely additive changes should be started at all?

Yes. See http://article.gmane.org/gmane.comp.lang.swift.evolution/21198

···

On Jul 5, 2016, at 12:03 PM, Tino Heth <2th@gmx.de> wrote:

Similarly, general discussions on this mailing list about blue sky additions to the language are distracting from our important goals, and the core team generally isn’t paying attention to these anyway. I would really appreciate it if we could stay focused on what is important, until the time is right to look beyond Swift 3. This time is in August [...]

Best regards,
Tino


(Tino) #7

Thanks for the link — imho it's really hard to keep an overview of this list…

So, I'll follow the leader :wink: and delay further posts on this topic

Not everything can be solved with protocols (stored properties, anyone?)

Sure, it's not completely painless, but you can declare

internal protocol MyClassProtocol {
  var foo: Bar { get set }
}

public class MyClass: MyClassProtocol {
  public var foo: Bar
}

It's 1 extra line of code. You declare the contract on stored properties by the protocol and just declare them on the class.

… but this single line only covers a single property in a single class, and you have to implement willSet/didSet in every class if you depend on this behavior.
Protocols are nice, and if support for generics is added in the future, a whole set of problems could be covered with those — but imho even protocols are no silver bullet, and there are situations where they are not the right tool.

IMHO if you need to hide the hierarchy *that* bad, it usually points to a bad design.

I don't believe in fighting developers with obscurity, so for me, it is not about the hiding, but about clarity:
There is no clear way to express that a certain class shouldn't be used outside its library without hiding all subclasses, too (well, you could write documentation… but no one likes documentation :wink:

Best regards,
Tino


Should not be public because its superclass is internal
(Karl) #8

It certainly would be nice to selectively hide parts of the class hierarchy.

Karl

···

On 5 Jul 2016, at 08:49, Andre via swift-evolution <swift-evolution@swift.org> wrote:

Personally, it's perhaps more of an "aesthetic" thing I suppose but I would definitely prefer that my internal classes stay internal even if there is a public subclass... I don't like how that leaks my internal implementation like that...

Though maybe the more swift thing to do would be to use private extensions in place of private superclasses I suppose...

andre

iPhoneから送信

2016/07/04 21:31、Tino Heth via swift-evolution <swift-evolution@swift.org> のメッセージ:

I'm running into "class cannot be declared public because its superclass is internal" issues on a regular basis, and I wonder if it wouldn't make sense to allow this combination:
It might be less useful as soon as there are abstract classes or generic protocols, but even then I think I'd like to have this "feature".

Inheritance can be such a private thing ;-), so imho there should be an obvious way to hide it (marking all init-methods internal works, but I don't think this is a good way to express the intention).

Tino
_______________________________________________
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


(John McCall) #9

I agree that it's theoretically okay to hide parts of the hierarchy. It would raise some secondary questions, like how to expose the "public" features of the internal superclass, but we could solve them.

However, it's clearly additive to do this, since it's just lifting a restriction, so please wait to propose this until Swift 3 is complete.

John.

···

On Jul 5, 2016, at 6:48 PM, Karl via swift-evolution <swift-evolution@swift.org> wrote:

On 5 Jul 2016, at 08:49, Andre via swift-evolution <swift-evolution@swift.org> wrote:

Personally, it's perhaps more of an "aesthetic" thing I suppose but I would definitely prefer that my internal classes stay internal even if there is a public subclass... I don't like how that leaks my internal implementation like that...

Though maybe the more swift thing to do would be to use private extensions in place of private superclasses I suppose...

andre

iPhoneから送信

2016/07/04 21:31、Tino Heth via swift-evolution <swift-evolution@swift.org> のメッセージ:

I'm running into "class cannot be declared public because its superclass is internal" issues on a regular basis, and I wonder if it wouldn't make sense to allow this combination:
It might be less useful as soon as there are abstract classes or generic protocols, but even then I think I'd like to have this "feature".

Inheritance can be such a private thing ;-), so imho there should be an obvious way to hide it (marking all init-methods internal works, but I don't think this is a good way to express the intention).

Tino
_______________________________________________
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

It certainly would be nice to selectively hide parts of the class hierarchy.


Allow subclass to be more visible than its superclass