Proposal: Sealed protocols


(Felix Gabel) #1

Hey everyone,

I hereby propose adding a ’sealed’ modifier for protocols to enable library developers to declare protocols that can be only conformed to in the scope of the library itself. This is similar to a ’sealed trait’ in Scala with the difference that it does not limit the protocol to be only applicable in the file it is declared in.

Example:

public sealed protocol FooType {
    // this protocol can be conformed to only in scope of the library itself
    // it can NOT be conformed to by any object declared outside the library
}

What do you think?

Thank you for your time,
Felix


(Douglas Gregor) #2

Can you elaborate on why a Swift developer would want to distinguish between “public sealed” and “internal”?

  - Doug

···

On Dec 3, 2015, at 2:59 PM, Felix Gabel <felix.gabel@me.com> wrote:

Hey everyone,

I hereby propose adding a ’sealed’ modifier for protocols to enable library developers to declare protocols that can be only conformed to in the scope of the library itself. This is similar to a ’sealed trait’ in Scala with the difference that it does not limit the protocol to be only applicable in the file it is declared in.

Example:

public sealed protocol FooType {
   // this protocol can be conformed to only in scope of the library itself
   // it can NOT be conformed to by any object declared outside the library
}

What do you think?


(Lily Ballard) #3

An alternative that would have the same effect is to allow protocols to
require conformance to other protocols with a lesser visibility. Because
no external type could conform to the private/internal protocol
requirement, they can't declare conformance to the public protocol
either. The only way they could conform is if the library adds
conformance to the private protocol to some public class, and then
external code can conform to the public protocol in subclasses, which
seems like a potentially-desirable thing (just yesterday someone asked
on IRC if there was a way to declare a protocol that could only be
conformed-to by UIViewController subclasses).

This would look something like

private protocol Restricted {}

public protocol SealedProto: Restricted {
    // ...
}

The best part about this proposal is it doesn't require adding any new
syntax or rules to the language, it only requires relaxing the rule that
says a protocol cannot require conformance to another protocol of lesser
visibility.

-Kevin Ballard

···

On Thu, Dec 3, 2015, at 02:59 PM, Felix Gabel wrote:

Hey everyone,

I hereby propose adding a ’sealed’ modifier for protocols to enable
library developers to declare protocols that can be only conformed to in
the scope of the library itself. This is similar to a ’sealed trait’ in
Scala with the difference that it does not limit the protocol to be only
applicable in the file it is declared in.

Example:

public sealed protocol FooType {
    // this protocol can be conformed to only in scope of the library
    itself
    // it can NOT be conformed to by any object declared outside the
    library
}

What do you think?

Thank you for your time,
Felix
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Mehdi Amini) #4

I can foresee how a whole module optimization would be able to do better devirtualization in this case, knowing the full possible set of type of instance.

···

On Dec 3, 2015, at 3:09 PM, Douglas Gregor <dgregor@apple.com> wrote:

On Dec 3, 2015, at 2:59 PM, Felix Gabel <felix.gabel@me.com> wrote:

Hey everyone,

I hereby propose adding a ’sealed’ modifier for protocols to enable library developers to declare protocols that can be only conformed to in the scope of the library itself. This is similar to a ’sealed trait’ in Scala with the difference that it does not limit the protocol to be only applicable in the file it is declared in.

Example:

public sealed protocol FooType {
  // this protocol can be conformed to only in scope of the library itself
  // it can NOT be conformed to by any object declared outside the library
}

What do you think?

Can you elaborate on why a Swift developer would want to distinguish between “public sealed” and “internal”?


Mehdi


(Joe Groff) #5

My first reaction is that enums already provide a way to model closed sum types. Enums do have some syntactic disadvantages compared to protocol types, since there's no automatic subtyping with enums like there are with protocols, but it might be worth considering ways we can improve that rather than adding redundant language features.

-Joe

···

On Dec 3, 2015, at 2:59 PM, Felix Gabel <felix.gabel@me.com> wrote:

Hey everyone,

I hereby propose adding a ’sealed’ modifier for protocols to enable library developers to declare protocols that can be only conformed to in the scope of the library itself. This is similar to a ’sealed trait’ in Scala with the difference that it does not limit the protocol to be only applicable in the file it is declared in.

Example:

public sealed protocol FooType {
   // this protocol can be conformed to only in scope of the library itself
   // it can NOT be conformed to by any object declared outside the library
}

What do you think?


(Dmitri Gribenko) #6

Hey everyone,

I hereby propose adding a ’sealed’ modifier for protocols to enable

library developers to declare protocols that can be only conformed to in
the scope of the library itself. This is similar to a ’sealed trait’ in
Scala with the difference that it does not limit the protocol to be only
applicable in the file it is declared in.

Example:

public sealed protocol FooType {
   // this protocol can be conformed to only in scope of the library

itself

   // it can NOT be conformed to by any object declared outside the

library

}

What do you think?

Can you elaborate on why a Swift developer would want to distinguish

between “public sealed” and “internal”?

There are cases when the library isn't designed to support new conformances
for the protocol, but the protocol is public because other public APIs are
expressed in terms of it.

We have a case for it in the standard library, 'protocol
AnyCollectionType'. Foundation also has a use case -- property list types.

Dmitri

···

On Thu, Dec 3, 2015 at 3:09 PM, Douglas Gregor <dgregor@apple.com> wrote:

On Dec 3, 2015, at 2:59 PM, Felix Gabel <felix.gabel@me.com> wrote:

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/


(Douglas Gregor) #7

Are you perhaps thinking of the different between “public” and “public sealed”? Both “public sealed” and “internal” allow whole module optimization to know the complete set of types that conform to the protocol. In the former case, that’s true because it’s the semantics of “sealed”; in the latter case, that’s true because the protocol isn’t visible outside of the module.

  - Doug

···

On Dec 3, 2015, at 3:13 PM, Mehdi Amini <mehdi.amini@apple.com> wrote:

On Dec 3, 2015, at 3:09 PM, Douglas Gregor <dgregor@apple.com> wrote:

On Dec 3, 2015, at 2:59 PM, Felix Gabel <felix.gabel@me.com> wrote:

Hey everyone,

I hereby propose adding a ’sealed’ modifier for protocols to enable library developers to declare protocols that can be only conformed to in the scope of the library itself. This is similar to a ’sealed trait’ in Scala with the difference that it does not limit the protocol to be only applicable in the file it is declared in.

Example:

public sealed protocol FooType {
// this protocol can be conformed to only in scope of the library itself
// it can NOT be conformed to by any object declared outside the library
}

What do you think?

Can you elaborate on why a Swift developer would want to distinguish between “public sealed” and “internal”?

I can foresee how a whole module optimization would be able to do better devirtualization in this case, knowing the full possible set of type of instance.


(Mehdi Amini) #8

Oh yes you’re right, I meant it for the difference between “public” and “public sealed”, but that wasn’t your question…

···

On Dec 3, 2015, at 3:33 PM, Douglas Gregor <dgregor@apple.com> wrote:

On Dec 3, 2015, at 3:13 PM, Mehdi Amini <mehdi.amini@apple.com <mailto:mehdi.amini@apple.com>> wrote:

On Dec 3, 2015, at 3:09 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

On Dec 3, 2015, at 2:59 PM, Felix Gabel <felix.gabel@me.com <mailto:felix.gabel@me.com>> wrote:

Hey everyone,

I hereby propose adding a ’sealed’ modifier for protocols to enable library developers to declare protocols that can be only conformed to in the scope of the library itself. This is similar to a ’sealed trait’ in Scala with the difference that it does not limit the protocol to be only applicable in the file it is declared in.

Example:

public sealed protocol FooType {
// this protocol can be conformed to only in scope of the library itself
// it can NOT be conformed to by any object declared outside the library
}

What do you think?

Can you elaborate on why a Swift developer would want to distinguish between “public sealed” and “internal”?

I can foresee how a whole module optimization would be able to do better devirtualization in this case, knowing the full possible set of type of instance.

Are you perhaps thinking of the different between “public” and “public sealed”? Both “public sealed” and “internal” allow whole module optimization to know the complete set of types that conform to the protocol. In the former case, that’s true because it’s the semantics of “sealed”; in the latter case, that’s true because the protocol isn’t visible outside of the module.


Mehdi


(Joe Groff) #9

Only vague notions, nothing ready to formally propose. Optional has hardcoded support for being a supertype of its `Some` payload; it might be worth surfacing that as a language feature other enums can also take advantage of:

enum Optional<Wrapped> {
  // Strawman: 'sub case' declares a case whose type becomes a subtype of the enum
  sub case Some(Wrapped)
  case None
}
enum Result<Success, Error: ErrorType> {
  sub case OK(Success)
  case Error(Error)
}

There would need to be constraints so that two 'sub cases' are never able to have overlapping payload types—otherwise with something like Either<Int, Int>, it'd be ambiguous which side of Either Int promotes to. OTOH, optional subtyping also causes some issues (1 < nil works being one of the more prominent ones), so we may not want to dig the subtyping hole deeper either…like I said, only vague notions.

-Joe

···

On Dec 3, 2015, at 3:32 PM, Anandabits <matthew@anandabits.com> wrote:

There are cases when the library isn't designed to support new conformances for the protocol, but the protocol is public because other public APIs are expressed in terms of it.

We have a case for it in the standard library, 'protocol AnyCollectionType'. Foundation also has a use case -- property list types.

I have had cases for this as well. Joe Groff's suggestion of adding more convenient syntax for sum types as an alternative may be an acceptable alternative for the cases I have seen, although I would need to have a better idea of what that might look like. Joe, have you written a proposal for this?


Implicit promotion of Optional
(Matthew Johnson) #10

There are cases when the library isn't designed to support new conformances for the protocol, but the protocol is public because other public APIs are expressed in terms of it.

We have a case for it in the standard library, 'protocol AnyCollectionType'. Foundation also has a use case -- property list types.

I have had cases for this as well. Joe Groff's suggestion of adding more convenient syntax for sum types as an alternative may be an acceptable alternative for the cases I have seen, although I would need to have a better idea of what that might look like. Joe, have you written a proposal for this?

Matthew


(Felix Gabel) #11

The problem using an enum in this case is that the overall complexity will increase with every case that is being introduced. Using a sealed protocol a library developer can define a set of conforming types, that is applicable for usage.

···

On Dec 4, 2015, at 12:13 AM, Joe Groff <jgroff@apple.com> wrote:

On Dec 3, 2015, at 2:59 PM, Felix Gabel <felix.gabel@me.com> wrote:

Hey everyone,

I hereby propose adding a ’sealed’ modifier for protocols to enable library developers to declare protocols that can be only conformed to in the scope of the library itself. This is similar to a ’sealed trait’ in Scala with the difference that it does not limit the protocol to be only applicable in the file it is declared in.

Example:

public sealed protocol FooType {
  // this protocol can be conformed to only in scope of the library itself
  // it can NOT be conformed to by any object declared outside the library
}

What do you think?

My first reaction is that enums already provide a way to model closed sum types. Enums do have some syntactic disadvantages compared to protocol types, since there's no automatic subtyping with enums like there are with protocols, but it might be worth considering ways we can improve that rather than adding redundant language features.

-Joe


(Felix Gabel) #12

- protocols starting with an underscore (_ArrayBufferType, _IntegerType, etc) would benefit from the ’sealed’ modifier, because the contract right now only implicitly says: ‘public but please don’t use’.
- another use case is grouping a set of types regardless their origin and limit the ability to extend the provided number of types by external forces

···

On Dec 4, 2015, at 12:25 AM, Dmitri Gribenko <gribozavr@gmail.com> wrote:

On Thu, Dec 3, 2015 at 3:09 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:
>
>> On Dec 3, 2015, at 2:59 PM, Felix Gabel <felix.gabel@me.com <mailto:felix.gabel@me.com>> wrote:
>>
>> Hey everyone,
>>
>> I hereby propose adding a ’sealed’ modifier for protocols to enable library developers to declare protocols that can be only conformed to in the scope of the library itself. This is similar to a ’sealed trait’ in Scala with the difference that it does not limit the protocol to be only applicable in the file it is declared in.
>>
>> Example:
>>
>> public sealed protocol FooType {
>> // this protocol can be conformed to only in scope of the library itself
>> // it can NOT be conformed to by any object declared outside the library
>> }
>>
>> What do you think?
>
>
> Can you elaborate on why a Swift developer would want to distinguish between “public sealed” and “internal”?

There are cases when the library isn't designed to support new conformances for the protocol, but the protocol is public because other public APIs are expressed in terms of it.

We have a case for it in the standard library, 'protocol AnyCollectionType'. Foundation also has a use case -- property list types.

Dmitri


(Adrian Kashivskyy) #13

protocols starting with an underscore (_ArrayBufferType, _IntegerType, etc) would benefit from the ’sealed’ modifier, because the contract right now only implicitly says: ‘public but please don’t use’.

I think it's wiser to refactor those protocols to be public and fully usable than to insert another access modifier to a language. Contracts that say "public but please don’t use" are generally a sign of design that is poor or limited by language/compiler.

Pozdrawiam – Regards,
Adrian Kashivskyy

···

Wiadomość napisana przez Felix Gabel <felix.gabel@me.com> w dniu 04.12.2015, o godz. 01:45:

On Dec 4, 2015, at 12:25 AM, Dmitri Gribenko <gribozavr@gmail.com <mailto:gribozavr@gmail.com>> wrote:

On Thu, Dec 3, 2015 at 3:09 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:
>
>> On Dec 3, 2015, at 2:59 PM, Felix Gabel <felix.gabel@me.com <mailto:felix.gabel@me.com>> wrote:
>>
>> Hey everyone,
>>
>> I hereby propose adding a ’sealed’ modifier for protocols to enable library developers to declare protocols that can be only conformed to in the scope of the library itself. This is similar to a ’sealed trait’ in Scala with the difference that it does not limit the protocol to be only applicable in the file it is declared in.
>>
>> Example:
>>
>> public sealed protocol FooType {
>> // this protocol can be conformed to only in scope of the library itself
>> // it can NOT be conformed to by any object declared outside the library
>> }
>>
>> What do you think?
>
>
> Can you elaborate on why a Swift developer would want to distinguish between “public sealed” and “internal”?

There are cases when the library isn't designed to support new conformances for the protocol, but the protocol is public because other public APIs are expressed in terms of it.

We have a case for it in the standard library, 'protocol AnyCollectionType'. Foundation also has a use case -- property list types.

Dmitri

- protocols starting with an underscore (_ArrayBufferType, _IntegerType, etc) would benefit from the ’sealed’ modifier, because the contract right now only implicitly says: ‘public but please don’t use’.
- another use case is grouping a set of types regardless their origin and limit the ability to extend the provided number of types by external forces

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