Ability to mark a class as publicly final while remaining internally non-final


(Thorsten Seitz) #1

I think the underlying problem is that you cannot “internally” conform to the internal protocol.

Desirable would be something like the following:

public class SomePublicClass: internal SomeInternalProtocol { // note internal conformance
public convenience init(someOtherParameter: Int) {
self.init(parameter: SomeInternalClass())
}
internal required init(parameter: SomeInternalClass) {
// do something
}
}

i.e. when conforming internally the required init would only have to have internal visibility and you could introduce a convenience init for the public API. SomePublicClass would only be usable as SomeInternalProtocol within the module.

Actually I think we would have to restrict the protocol to have the same visibility to avoid problems (i.e. it would not be possible to internally conform to a public protocol). This would make the access modifier in the conformance clause redundant, of course, so the proposal would effectively just allow conforming to a protocol with lower visibility, resulting in the conformance to have just that reduced visibility.

-Thorsten

···

Am 25. April 2016 um 08:27 schrieb Matt Comi via swift-evolution swift-evolution@swift.org:

Consider a public class that conforms to an internal protocol that has an initializer requirement:

internal class SomeInternalClass { }

internal protocol SomeInternalProtocol {
init(parameter: SomeInternalClass)
}

public class SomePublicClass: SomeInternalProtocol {
// compiler error: initializer requirement ‘init(parameter:)’ can only be satisfied by a required initializer in non-final class ‘SomePublicClass’
init(parameter: SomeInternalClass) { }
}

To resolve this error, init(parameter:) must be made public required, or SomePublicClass must be made final.

If init(parameter:) were made public required, it would expose the module’s internal structure; SomeInternalClass would need to be made public also. This makes sense, of course; if SomePublicClass is public and non-final, it may be subclassed outside the module and any subclass would need to know about init(parameter:) in order to remain conformant to SomeInternalProtocol.

Alternatively, If SomePublicClass were made final, it could not be subclassed outside the module and thus, SomeInternalClass would not need to be exposed, but subclassing SomePublicClass within the module would also be impossible also.

I propose that it should be possible to mark a class as publicly final while remaining internally non-final. This would resolve both issues: SomeInternalClass can remain internal, and SomePublicClass can be subclassed within the module.


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


(Matt Comi) #2

I think the underlying problem is that you cannot "internally" conform to the internal protocol.

Desirable would be something like the following:

public class SomePublicClass: internal SomeInternalProtocol { // note internal conformance
  public convenience init(someOtherParameter: Int) {
    self.init(parameter: SomeInternalClass())
  }
  internal required init(parameter: SomeInternalClass) {
      // do something
  }
}

i.e. when conforming internally the required init would only have to have internal visibility and you could introduce a convenience init for the public API. SomePublicClass would only be usable as SomeInternalProtocol within the module.

I like that. So I guess, more broadly, my proposal is:

Provide a mechanism that allows a non-final class to conform to an internal protocol that has an initializer requirement without needing to expose that initializer publicly.

And to recap, my suggestion was:

- Allow a class to be marked publicly final, but internally non-final.

And yours was:

- Allow a class to “internally” conform to a protocol.

Both approaches solve the same problem. Anyone got any other thoughts? Thanks.

Matt

···

On 25 Apr 2016, at 3:08 PM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org> wrote:

Am 25. April 2016 um 08:27 schrieb Matt Comi via swift-evolution <swift-evolution@swift.org>:

Consider a public class that conforms to an internal protocol that has an initializer requirement:

internal class SomeInternalClass { }

internal protocol SomeInternalProtocol {
init(parameter: SomeInternalClass)
}

public class SomePublicClass: SomeInternalProtocol {
// compiler error: initializer requirement ‘init(parameter:)’ can only be satisfied by a `required` initializer in non-final class ‘SomePublicClass’
init(parameter: SomeInternalClass) { }
}

To resolve this error, init(parameter:) must be made public required, or SomePublicClass must be made final.

If init(parameter:) were made public required, it would expose the module's internal structure; SomeInternalClass would need to be made public also. This makes sense, of course; if SomePublicClass is public and non-final, it may be subclassed outside the module and any subclass would need to know about init(parameter:) in order to remain conformant to SomeInternalProtocol.

Alternatively, If SomePublicClass were made final, it could not be subclassed outside the module and thus, SomeInternalClass would not need to be exposed, but subclassing SomePublicClass within the module would also be impossible also.

I propose that it should be possible to mark a class as publicly final while remaining internally non-final. This would resolve both issues: SomeInternalClass can remain internal, and SomePublicClass can be subclassed within the module.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto: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