Subclass Existentials


(David Hart) #1

Hello,

As promised, I wrote the first draft of a proposal to add class requirements to the existential syntax. Please let me know what you think.

https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md

Regards,
David.

Existentials for classes conforming to protocols

Proposal: SE-XXXX <https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md>
Authors: David Hart <http://github.com/hartbit/>, Austin Zheng <http://github.com/austinzheng>
Review Manager: TBD
Status: TBD
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#introduction>Introduction

This proposal brings more expressive power to the type system by allowing Swift to represent existentials of classes and subclasses which conform to protocols.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#motivation>Motivation

Currently, the only existentials which can be represented in Swift are conformances to a set of protocols, using the &syntax:

let existential: Hashable & CustomStringConvertible
On the other hand, Objective-C is capable of expressing existentials of subclasses conforming to protocols with the following syntax:

UIViewController<UITableViewDataSource, UITableViewDelegate>* existential;
We propose to provide similar expressive power to Swift, which will also improve the bridging of those types from Objective-C.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#proposed-solution>Proposed solution

The proposal keeps the existing & syntax but allows the first element, and only the first, to be of class type. The equivalent declaration to the above Objective-C declaration would look like this:

let existential: UIViewController & UITableViewDataSource & UITableViewDelegate
As in Objective-C, this existential represents classes which have UIViewController in their parent inheritance hierarchy and which also conform to the UITableViewDataSource and UITableViewDelegate protocols.

As only the first element in the existential composition syntax can be a class type, and by extending this rule to typealias expansions, we can make sure that we only need to read the first element to know if it contains a class requirement. As a consequence, here is a list of valid and invalid code and the reasons for them:

let a: Hashable & CustomStringConvertible
// VALID: This is still valid, as before

let b: MyObject & Hashable
// VALID: This is the new rule which allows an object type in first position

let c: CustomStringConvertible & MyObject
// INVALID: MyObject is not allowed in second position. A fix-it should help transform it to:
// let c: MyObject & CustomStringConvertible

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: Hashable & MyObjectStringConvertible
// INVALID: The typealias expansion means that the type of d expands to Hashable & MyObject & CustomStringConvertible, which has the class in the wrong position. A fix-it should help transform it to:
// let d: MyObjectStringConvertible & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let e: MyOtherObject & MyObjectStringConvertible
// INVALID: The typealias expansion would allow an existential with two class requirements, which is invalid
The following examples could technically be legal, but we believe we should keep them invalid to keep the rules simple:

let a: MyObject & MyObject & CustomStringConvertible
// This is equivalent to MyObject & CustomStringConvertible

let b: MyObjectSubclass & MyObject & Hashable
// This is equivalent to MyObjectSubclass & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: MyObject & MyObjectStringConvertible
// This is equivalent to MyObject & CustomStringConvertible
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#source-compatibility>Source compatibility

This is a source breaking change. All types bridged from Objective-C which use the equivalent Objective-C feature import without the protocol conformances in Swift 3. This change would increase the existential's requirement and break on code which does not meet the new protocol requirements. For example, the following Objective-C code:

@interface MyViewController
- (void)setup:(nonnull UIViewController<UITableViewDataSource,UITableViewDelegate>*)tableViewController;
@end
is imported into Swift 3 as:

class MyViewController {
    func setup(tableViewController: UIViewController) {}
}
which allows calling the function with an invalid parameter:

let myViewController: MyViewController()
myViewController.setup(UIViewController())
The previous code would have worked as long as the Objective-C code did not call any method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code would now be imported as:

class MyViewController {
    func setup(tableViewController: UIViewController & UITableViewDataSource & UITableViewDelegate) {}
}
That would then cause the Swift code to fail to compile with an error which states that UIViewController does not conform to the UITableViewDataSource and UITableViewDelegate protocols.

It is a source-breaking change, but should have a minimal impact for the following reasons:

Not many Objective-C code used the existential syntax in practice.
There generated errors are a good thing because they point out potential crashes which would have gone un-noticed.
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#alternatives-considered>Alternatives considered

None.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#acknowledgements>Acknowledgements

Thanks to Austin Zheng <http://github.com/austinzheng> and Matthew Johnson <https://github.com/anandabits> who brought a lot of attention to existentials in this mailing-list and from whom most of the ideas in the proposal come from.


(Kevin Nattinger) #2

+1 on including concrete types, this is currently a gaping hole in the language.
-100 on requiring the concrete type to be first. If I want to specify the types alphabetically, grouped by module, or any other way I choose, why should I not be allowed to do so? The compiler can warn on redundancies and error on contradictions, that’s all we really need.

···

On Jan 29, 2017, at 8:39 AM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

Hello,

As promised, I wrote the first draft of a proposal to add class requirements to the existential syntax. Please let me know what you think.

https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md

Regards,
David.

Existentials for classes conforming to protocols

Proposal: SE-XXXX <https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md>
Authors: David Hart <http://github.com/hartbit/>, Austin Zheng <http://github.com/austinzheng>
Review Manager: TBD
Status: TBD
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#introduction>Introduction

This proposal brings more expressive power to the type system by allowing Swift to represent existentials of classes and subclasses which conform to protocols.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#motivation>Motivation

Currently, the only existentials which can be represented in Swift are conformances to a set of protocols, using the &syntax:

let existential: Hashable & CustomStringConvertible
On the other hand, Objective-C is capable of expressing existentials of subclasses conforming to protocols with the following syntax:

UIViewController<UITableViewDataSource, UITableViewDelegate>* existential;
We propose to provide similar expressive power to Swift, which will also improve the bridging of those types from Objective-C.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#proposed-solution>Proposed solution

The proposal keeps the existing & syntax but allows the first element, and only the first, to be of class type. The equivalent declaration to the above Objective-C declaration would look like this:

let existential: UIViewController & UITableViewDataSource & UITableViewDelegate
As in Objective-C, this existential represents classes which have UIViewController in their parent inheritance hierarchy and which also conform to the UITableViewDataSource and UITableViewDelegate protocols.

As only the first element in the existential composition syntax can be a class type, and by extending this rule to typealias expansions, we can make sure that we only need to read the first element to know if it contains a class requirement. As a consequence, here is a list of valid and invalid code and the reasons for them:

let a: Hashable & CustomStringConvertible
// VALID: This is still valid, as before

let b: MyObject & Hashable
// VALID: This is the new rule which allows an object type in first position

let c: CustomStringConvertible & MyObject
// INVALID: MyObject is not allowed in second position. A fix-it should help transform it to:
// let c: MyObject & CustomStringConvertible

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: Hashable & MyObjectStringConvertible
// INVALID: The typealias expansion means that the type of d expands to Hashable & MyObject & CustomStringConvertible, which has the class in the wrong position. A fix-it should help transform it to:
// let d: MyObjectStringConvertible & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let e: MyOtherObject & MyObjectStringConvertible
// INVALID: The typealias expansion would allow an existential with two class requirements, which is invalid
The following examples could technically be legal, but we believe we should keep them invalid to keep the rules simple:

let a: MyObject & MyObject & CustomStringConvertible
// This is equivalent to MyObject & CustomStringConvertible

let b: MyObjectSubclass & MyObject & Hashable
// This is equivalent to MyObjectSubclass & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: MyObject & MyObjectStringConvertible
// This is equivalent to MyObject & CustomStringConvertible
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#source-compatibility>Source compatibility

This is a source breaking change. All types bridged from Objective-C which use the equivalent Objective-C feature import without the protocol conformances in Swift 3. This change would increase the existential's requirement and break on code which does not meet the new protocol requirements. For example, the following Objective-C code:

@interface MyViewController
- (void)setup:(nonnull UIViewController<UITableViewDataSource,UITableViewDelegate>*)tableViewController;
@end
is imported into Swift 3 as:

class MyViewController {
    func setup(tableViewController: UIViewController) {}
}
which allows calling the function with an invalid parameter:

let myViewController: MyViewController()
myViewController.setup(UIViewController())
The previous code would have worked as long as the Objective-C code did not call any method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code would now be imported as:

class MyViewController {
    func setup(tableViewController: UIViewController & UITableViewDataSource & UITableViewDelegate) {}
}
That would then cause the Swift code to fail to compile with an error which states that UIViewController does not conform to the UITableViewDataSource and UITableViewDelegate protocols.

It is a source-breaking change, but should have a minimal impact for the following reasons:

Not many Objective-C code used the existential syntax in practice.
There generated errors are a good thing because they point out potential crashes which would have gone un-noticed.
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#alternatives-considered>Alternatives considered

None.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#acknowledgements>Acknowledgements

Thanks to Austin Zheng <http://github.com/austinzheng> and Matthew Johnson <https://github.com/anandabits> who brought a lot of attention to existentials in this mailing-list and from whom most of the ideas in the proposal come from.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #3

Hi David,

This looks like a great start.

One thing we should consider is whether we tie this proposal so tightly to classes or whether it might be better to call these supertype constraints. The feature might also be useful for value types if / when Swift gets value subtyping.

One enhancement that might be worth considering. Specifically, allowing protocols to declare a specific supertype requirement in the place where a `class` constraint would usually be found. After this proposal we could already do something similar using a `class` constraint to define a protocol and a typealias to bind it to the supertype requirement. It seems like allowing users to state this more directly would be a good idea.

As only the first element in the existential composition syntax can be a class type, and by extending this rule to typealias expansions, we can make sure that we only need to read the first element to know if it contains a class requirement.

I think this is unnecessarily limiting in a couple of ways. I agree that a class should come first if it is mentioned explicitly***. I am less sure we should require this when the type is part of a typealias combined with other protocol requirements.

For example, one use case I remember discussing with Austin is refining supertype requirements. If I have a typealias which requires a superclass `Base` I should be able to form an existential using that typealias that *refines* that requirement to some type *Derived* which is a non-final subtype of `Base`. This would require syntax that allows us to put a class name in the first position, but also mention a typealias with a supertype requirement in a subsequent position.

Matthew

*** One argument against requiring a class to come first is that we could conceptualize `&` as a type operator with a handful of overloads. This would include both lhs and rhs are “protocol only kinds” as well as overloads with a “protocol only kind” in either lhs or rhs and a “supertype kind” in the other position. The tricky part of pulling this off would be including an overload where both lhs and rhs have a “supertype kind”, but only when the operands have a subtype / supertype relationship with each other.

I suspect this conceptualization isn’t worth the complexity it brings, but it is tempting to try and view `&` as a type operator. As long as this only involves a loosening of restrictions it could probably be introduced as an additive change down the road.

···

On Jan 29, 2017, at 10:39 AM, David Hart <david@hartbit.com> wrote:

Hello,

As promised, I wrote the first draft of a proposal to add class requirements to the existential syntax. Please let me know what you think.

https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md

Regards,
David.

Existentials for classes conforming to protocols

Proposal: SE-XXXX <https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md>
Authors: David Hart <http://github.com/hartbit/>, Austin Zheng <http://github.com/austinzheng>
Review Manager: TBD
Status: TBD
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#introduction>Introduction

This proposal brings more expressive power to the type system by allowing Swift to represent existentials of classes and subclasses which conform to protocols.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#motivation>Motivation

Currently, the only existentials which can be represented in Swift are conformances to a set of protocols, using the &syntax:

let existential: Hashable & CustomStringConvertible
On the other hand, Objective-C is capable of expressing existentials of subclasses conforming to protocols with the following syntax:

UIViewController<UITableViewDataSource, UITableViewDelegate>* existential;
We propose to provide similar expressive power to Swift, which will also improve the bridging of those types from Objective-C.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#proposed-solution>Proposed solution

The proposal keeps the existing & syntax but allows the first element, and only the first, to be of class type. The equivalent declaration to the above Objective-C declaration would look like this:

let existential: UIViewController & UITableViewDataSource & UITableViewDelegate
As in Objective-C, this existential represents classes which have UIViewController in their parent inheritance hierarchy and which also conform to the UITableViewDataSource and UITableViewDelegate protocols.

As only the first element in the existential composition syntax can be a class type, and by extending this rule to typealias expansions, we can make sure that we only need to read the first element to know if it contains a class requirement. As a consequence, here is a list of valid and invalid code and the reasons for them:

let a: Hashable & CustomStringConvertible
// VALID: This is still valid, as before

let b: MyObject & Hashable
// VALID: This is the new rule which allows an object type in first position

let c: CustomStringConvertible & MyObject
// INVALID: MyObject is not allowed in second position. A fix-it should help transform it to:
// let c: MyObject & CustomStringConvertible

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: Hashable & MyObjectStringConvertible
// INVALID: The typealias expansion means that the type of d expands to Hashable & MyObject & CustomStringConvertible, which has the class in the wrong position. A fix-it should help transform it to:
// let d: MyObjectStringConvertible & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let e: MyOtherObject & MyObjectStringConvertible
// INVALID: The typealias expansion would allow an existential with two class requirements, which is invalid
The following examples could technically be legal, but we believe we should keep them invalid to keep the rules simple:

let a: MyObject & MyObject & CustomStringConvertible
// This is equivalent to MyObject & CustomStringConvertible

let b: MyObjectSubclass & MyObject & Hashable
// This is equivalent to MyObjectSubclass & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: MyObject & MyObjectStringConvertible
// This is equivalent to MyObject & CustomStringConvertible
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#source-compatibility>Source compatibility

This is a source breaking change. All types bridged from Objective-C which use the equivalent Objective-C feature import without the protocol conformances in Swift 3. This change would increase the existential's requirement and break on code which does not meet the new protocol requirements. For example, the following Objective-C code:

@interface MyViewController
- (void)setup:(nonnull UIViewController<UITableViewDataSource,UITableViewDelegate>*)tableViewController;
@end
is imported into Swift 3 as:

class MyViewController {
    func setup(tableViewController: UIViewController) {}
}
which allows calling the function with an invalid parameter:

let myViewController: MyViewController()
myViewController.setup(UIViewController())
The previous code would have worked as long as the Objective-C code did not call any method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code would now be imported as:

class MyViewController {
    func setup(tableViewController: UIViewController & UITableViewDataSource & UITableViewDelegate) {}
}
That would then cause the Swift code to fail to compile with an error which states that UIViewController does not conform to the UITableViewDataSource and UITableViewDelegate protocols.

It is a source-breaking change, but should have a minimal impact for the following reasons:

Not many Objective-C code used the existential syntax in practice.
There generated errors are a good thing because they point out potential crashes which would have gone un-noticed.
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#alternatives-considered>Alternatives considered

None.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#acknowledgements>Acknowledgements

Thanks to Austin Zheng <http://github.com/austinzheng> and Matthew Johnson <https://github.com/anandabits> who brought a lot of attention to existentials in this mailing-list and from whom most of the ideas in the proposal come from.


(Chris Lattner) #4

I think it makes sense to require class bounds to be first, for the same reason they must be first in. Lass inheritance clauses: to encourage consistency and make it more clear when some type has an ambiguous name (which shouldn't happen, but...)

-Chris

···

On Jan 29, 2017, at 9:03 AM, Kevin Nattinger via swift-evolution <swift-evolution@swift.org> wrote:

+1 on including concrete types, this is currently a gaping hole in the language.
-100 on requiring the concrete type to be first. If I want to specify the types alphabetically, grouped by module, or any other way I choose, why should I not be allowed to do so? The compiler can warn on redundancies and error on contradictions, that’s all we really need.

On Jan 29, 2017, at 8:39 AM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

Hello,

As promised, I wrote the first draft of a proposal to add class requirements to the existential syntax. Please let me know what you think.

https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md

Regards,
David.

Existentials for classes conforming to protocols
Proposal: SE-XXXX
Authors: David Hart, Austin Zheng
Review Manager: TBD
Status: TBD
Introduction

This proposal brings more expressive power to the type system by allowing Swift to represent existentials of classes and subclasses which conform to protocols.

Motivation

Currently, the only existentials which can be represented in Swift are conformances to a set of protocols, using the &syntax:

let existential: Hashable & CustomStringConvertible
On the other hand, Objective-C is capable of expressing existentials of subclasses conforming to protocols with the following syntax:

UIViewController<UITableViewDataSource, UITableViewDelegate>* existential;
We propose to provide similar expressive power to Swift, which will also improve the bridging of those types from Objective-C.

Proposed solution

The proposal keeps the existing & syntax but allows the first element, and only the first, to be of class type. The equivalent declaration to the above Objective-C declaration would look like this:

let existential: UIViewController & UITableViewDataSource & UITableViewDelegate
As in Objective-C, this existential represents classes which have UIViewController in their parent inheritance hierarchy and which also conform to the UITableViewDataSource and UITableViewDelegate protocols.

As only the first element in the existential composition syntax can be a class type, and by extending this rule to typealias expansions, we can make sure that we only need to read the first element to know if it contains a class requirement. As a consequence, here is a list of valid and invalid code and the reasons for them:

let a: Hashable & CustomStringConvertible
// VALID: This is still valid, as before

let b: MyObject & Hashable
// VALID: This is the new rule which allows an object type in first position

let c: CustomStringConvertible & MyObject
// INVALID: MyObject is not allowed in second position. A fix-it should help transform it to:
// let c: MyObject & CustomStringConvertible

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: Hashable & MyObjectStringConvertible
// INVALID: The typealias expansion means that the type of d expands to Hashable & MyObject & CustomStringConvertible, which has the class in the wrong position. A fix-it should help transform it to:
// let d: MyObjectStringConvertible & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let e: MyOtherObject & MyObjectStringConvertible
// INVALID: The typealias expansion would allow an existential with two class requirements, which is invalid
The following examples could technically be legal, but we believe we should keep them invalid to keep the rules simple:

let a: MyObject & MyObject & CustomStringConvertible
// This is equivalent to MyObject & CustomStringConvertible

let b: MyObjectSubclass & MyObject & Hashable
// This is equivalent to MyObjectSubclass & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: MyObject & MyObjectStringConvertible
// This is equivalent to MyObject & CustomStringConvertible
Source compatibility

This is a source breaking change. All types bridged from Objective-C which use the equivalent Objective-C feature import without the protocol conformances in Swift 3. This change would increase the existential's requirement and break on code which does not meet the new protocol requirements. For example, the following Objective-C code:

@interface MyViewController
- (void)setup:(nonnull UIViewController<UITableViewDataSource,UITableViewDelegate>*)tableViewController;
@end
is imported into Swift 3 as:

class MyViewController {
    func setup(tableViewController: UIViewController) {}
}
which allows calling the function with an invalid parameter:

let myViewController: MyViewController()
myViewController.setup(UIViewController())
The previous code would have worked as long as the Objective-C code did not call any method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code would now be imported as:

class MyViewController {
    func setup(tableViewController: UIViewController & UITableViewDataSource & UITableViewDelegate) {}
}
That would then cause the Swift code to fail to compile with an error which states that UIViewController does not conform to the UITableViewDataSource and UITableViewDelegate protocols.

It is a source-breaking change, but should have a minimal impact for the following reasons:

Not many Objective-C code used the existential syntax in practice.
There generated errors are a good thing because they point out potential crashes which would have gone un-noticed.
Alternatives considered

None.

Acknowledgements

Thanks to Austin Zheng and Matthew Johnson who brought a lot of attention to existentials in this mailing-list and from whom most of the ideas in the proposal come from.
_______________________________________________
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


(Chris Lattner) #5

Sorry, I meant to say for the same reason they are required to be first in class inheritance/conformance clauses.

-Chris

···

On Jan 29, 2017, at 12:14 PM, Chris Lattner <sabre@nondot.org> wrote:

I think it makes sense to require class bounds to be first, for the same reason they must be first in. Lass inheritance clauses: to encourage consistency and make it more clear when some type has an ambiguous name (which shouldn't happen, but...)

-Chris

On Jan 29, 2017, at 9:03 AM, Kevin Nattinger via swift-evolution <swift-evolution@swift.org> wrote:

+1 on including concrete types, this is currently a gaping hole in the language.
-100 on requiring the concrete type to be first. If I want to specify the types alphabetically, grouped by module, or any other way I choose, why should I not be allowed to do so? The compiler can warn on redundancies and error on contradictions, that’s all we really need.

On Jan 29, 2017, at 8:39 AM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

Hello,

As promised, I wrote the first draft of a proposal to add class requirements to the existential syntax. Please let me know what you think.

https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md

Regards,
David.

Existentials for classes conforming to protocols
Proposal: SE-XXXX
Authors: David Hart, Austin Zheng
Review Manager: TBD
Status: TBD
Introduction

This proposal brings more expressive power to the type system by allowing Swift to represent existentials of classes and subclasses which conform to protocols.

Motivation

Currently, the only existentials which can be represented in Swift are conformances to a set of protocols, using the &syntax:

let existential: Hashable & CustomStringConvertible
On the other hand, Objective-C is capable of expressing existentials of subclasses conforming to protocols with the following syntax:

UIViewController<UITableViewDataSource, UITableViewDelegate>* existential;
We propose to provide similar expressive power to Swift, which will also improve the bridging of those types from Objective-C.

Proposed solution

The proposal keeps the existing & syntax but allows the first element, and only the first, to be of class type. The equivalent declaration to the above Objective-C declaration would look like this:

let existential: UIViewController & UITableViewDataSource & UITableViewDelegate
As in Objective-C, this existential represents classes which have UIViewController in their parent inheritance hierarchy and which also conform to the UITableViewDataSource and UITableViewDelegate protocols.

As only the first element in the existential composition syntax can be a class type, and by extending this rule to typealias expansions, we can make sure that we only need to read the first element to know if it contains a class requirement. As a consequence, here is a list of valid and invalid code and the reasons for them:

let a: Hashable & CustomStringConvertible
// VALID: This is still valid, as before

let b: MyObject & Hashable
// VALID: This is the new rule which allows an object type in first position

let c: CustomStringConvertible & MyObject
// INVALID: MyObject is not allowed in second position. A fix-it should help transform it to:
// let c: MyObject & CustomStringConvertible

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: Hashable & MyObjectStringConvertible
// INVALID: The typealias expansion means that the type of d expands to Hashable & MyObject & CustomStringConvertible, which has the class in the wrong position. A fix-it should help transform it to:
// let d: MyObjectStringConvertible & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let e: MyOtherObject & MyObjectStringConvertible
// INVALID: The typealias expansion would allow an existential with two class requirements, which is invalid
The following examples could technically be legal, but we believe we should keep them invalid to keep the rules simple:

let a: MyObject & MyObject & CustomStringConvertible
// This is equivalent to MyObject & CustomStringConvertible

let b: MyObjectSubclass & MyObject & Hashable
// This is equivalent to MyObjectSubclass & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: MyObject & MyObjectStringConvertible
// This is equivalent to MyObject & CustomStringConvertible
Source compatibility

This is a source breaking change. All types bridged from Objective-C which use the equivalent Objective-C feature import without the protocol conformances in Swift 3. This change would increase the existential's requirement and break on code which does not meet the new protocol requirements. For example, the following Objective-C code:

@interface MyViewController
- (void)setup:(nonnull UIViewController<UITableViewDataSource,UITableViewDelegate>*)tableViewController;
@end
is imported into Swift 3 as:

class MyViewController {
    func setup(tableViewController: UIViewController) {}
}
which allows calling the function with an invalid parameter:

let myViewController: MyViewController()
myViewController.setup(UIViewController())
The previous code would have worked as long as the Objective-C code did not call any method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code would now be imported as:

class MyViewController {
    func setup(tableViewController: UIViewController & UITableViewDataSource & UITableViewDelegate) {}
}
That would then cause the Swift code to fail to compile with an error which states that UIViewController does not conform to the UITableViewDataSource and UITableViewDelegate protocols.

It is a source-breaking change, but should have a minimal impact for the following reasons:

Not many Objective-C code used the existential syntax in practice.
There generated errors are a good thing because they point out potential crashes which would have gone un-noticed.
Alternatives considered

None.

Acknowledgements

Thanks to Austin Zheng and Matthew Johnson who brought a lot of attention to existentials in this mailing-list and from whom most of the ideas in the proposal come from.
_______________________________________________
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


(Chris Lattner) #6

+1 on the proposal overall. You might want to clarify the writing wrt source compatibility though. The new syntax and type system mechanics should work in Swift 3 mode, it is just the importer change that must be conditionalized.

-Chris

···

On Jan 29, 2017, at 8:39 AM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

Hello,

As promised, I wrote the first draft of a proposal to add class requirements to the existential syntax. Please let me know what you think.

https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md

Regards,
David.

Existentials for classes conforming to protocols
Proposal: SE-XXXX
Authors: David Hart, Austin Zheng
Review Manager: TBD
Status: TBD
Introduction

This proposal brings more expressive power to the type system by allowing Swift to represent existentials of classes and subclasses which conform to protocols.

Motivation

Currently, the only existentials which can be represented in Swift are conformances to a set of protocols, using the &syntax:

let existential: Hashable & CustomStringConvertible
On the other hand, Objective-C is capable of expressing existentials of subclasses conforming to protocols with the following syntax:

UIViewController<UITableViewDataSource, UITableViewDelegate>* existential;
We propose to provide similar expressive power to Swift, which will also improve the bridging of those types from Objective-C.

Proposed solution

The proposal keeps the existing & syntax but allows the first element, and only the first, to be of class type. The equivalent declaration to the above Objective-C declaration would look like this:

let existential: UIViewController & UITableViewDataSource & UITableViewDelegate
As in Objective-C, this existential represents classes which have UIViewController in their parent inheritance hierarchy and which also conform to the UITableViewDataSource and UITableViewDelegate protocols.

As only the first element in the existential composition syntax can be a class type, and by extending this rule to typealias expansions, we can make sure that we only need to read the first element to know if it contains a class requirement. As a consequence, here is a list of valid and invalid code and the reasons for them:

let a: Hashable & CustomStringConvertible
// VALID: This is still valid, as before

let b: MyObject & Hashable
// VALID: This is the new rule which allows an object type in first position

let c: CustomStringConvertible & MyObject
// INVALID: MyObject is not allowed in second position. A fix-it should help transform it to:
// let c: MyObject & CustomStringConvertible

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: Hashable & MyObjectStringConvertible
// INVALID: The typealias expansion means that the type of d expands to Hashable & MyObject & CustomStringConvertible, which has the class in the wrong position. A fix-it should help transform it to:
// let d: MyObjectStringConvertible & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let e: MyOtherObject & MyObjectStringConvertible
// INVALID: The typealias expansion would allow an existential with two class requirements, which is invalid
The following examples could technically be legal, but we believe we should keep them invalid to keep the rules simple:

let a: MyObject & MyObject & CustomStringConvertible
// This is equivalent to MyObject & CustomStringConvertible

let b: MyObjectSubclass & MyObject & Hashable
// This is equivalent to MyObjectSubclass & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: MyObject & MyObjectStringConvertible
// This is equivalent to MyObject & CustomStringConvertible
Source compatibility

This is a source breaking change. All types bridged from Objective-C which use the equivalent Objective-C feature import without the protocol conformances in Swift 3. This change would increase the existential's requirement and break on code which does not meet the new protocol requirements. For example, the following Objective-C code:

@interface MyViewController
- (void)setup:(nonnull UIViewController<UITableViewDataSource,UITableViewDelegate>*)tableViewController;
@end
is imported into Swift 3 as:

class MyViewController {
    func setup(tableViewController: UIViewController) {}
}
which allows calling the function with an invalid parameter:

let myViewController: MyViewController()
myViewController.setup(UIViewController())
The previous code would have worked as long as the Objective-C code did not call any method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code would now be imported as:

class MyViewController {
    func setup(tableViewController: UIViewController & UITableViewDataSource & UITableViewDelegate) {}
}
That would then cause the Swift code to fail to compile with an error which states that UIViewController does not conform to the UITableViewDataSource and UITableViewDelegate protocols.

It is a source-breaking change, but should have a minimal impact for the following reasons:

Not many Objective-C code used the existential syntax in practice.
There generated errors are a good thing because they point out potential crashes which would have gone un-noticed.
Alternatives considered

None.

Acknowledgements

Thanks to Austin Zheng and Matthew Johnson who brought a lot of attention to existentials in this mailing-list and from whom most of the ideas in the proposal come from.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #7

As Matthew mentioned, the rules can certainly later be relaxed, but given
that this proposal has the compiler generating fix-its for subclasses in
second position, is there a reason other than stylistic for demanding
MyClass & MyProtocol instead of MyProtocol & MyClass?

From a naive perspective, it seems that if the compiler understands my

meaning perfectly, it should just accept that spelling rather than complain.

···

On Sun, Jan 29, 2017 at 11:30 Matthew Johnson via swift-evolution < swift-evolution@swift.org> wrote:

Hi David,

This looks like a great start.

One thing we should consider is whether we tie this proposal so tightly to
classes or whether it might be better to call these supertype constraints.
The feature might also be useful for value types if / when Swift gets value
subtyping.

One enhancement that might be worth considering. Specifically, allowing
protocols to declare a specific supertype requirement in the place where a
`class` constraint would usually be found. After this proposal we could
already do something similar using a `class` constraint to define a
protocol and a typealias to bind it to the supertype requirement. It seems
like allowing users to state this more directly would be a good idea.

As only the first element in the existential composition syntax can be a
class type, and by extending this rule to typealias expansions, we can make
sure that we only need to read the first element to know if it contains a
class requirement.

I think this is unnecessarily limiting in a couple of ways. I agree that
a class should come first if it is mentioned explicitly***. I am less sure
we should require this when the type is part of a typealias combined with
other protocol requirements.

For example, one use case I remember discussing with Austin is refining
supertype requirements. If I have a typealias which requires a superclass
`Base` I should be able to form an existential using that typealias that
*refines* that requirement to some type *Derived* which is a non-final
subtype of `Base`. This would require syntax that allows us to put a class
name in the first position, but also mention a typealias with a supertype
requirement in a subsequent position.

Matthew

*** One argument against requiring a class to come first is that we could
conceptualize `&` as a type operator with a handful of overloads. This
would include both lhs and rhs are “protocol only kinds” as well as
overloads with a “protocol only kind” in either lhs or rhs and a “supertype
kind” in the other position. The tricky part of pulling this off would be
including an overload where both lhs and rhs have a “supertype kind”, but
only when the operands have a subtype / supertype relationship with each
other.

I suspect this conceptualization isn’t worth the complexity it brings, but
it is tempting to try and view `&` as a type operator. As long as this
only involves a loosening of restrictions it could probably be introduced
as an additive change down the road.

On Jan 29, 2017, at 10:39 AM, David Hart <david@hartbit.com> wrote:

Hello,

As promised, I wrote the first draft of a proposal to add class
requirements to the existential syntax. Please let me know what you think.

https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md

Regards,
David.

Existentials for classes conforming to protocols

   - Proposal: SE-XXXX
   <https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md>
   - Authors: David Hart <http://github.com/hartbit/>, Austin Zheng
   <http://github.com/austinzheng>
   - Review Manager: TBD
   - Status: TBD

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#introduction>
Introduction

This proposal brings more expressive power to the type system by allowing
Swift to represent existentials of classes and subclasses which conform to
protocols.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#motivation>
Motivation

Currently, the only existentials which can be represented in Swift are
conformances to a set of protocols, using the &syntax:

let existential: Hashable & CustomStringConvertible

On the other hand, Objective-C is capable of expressing existentials of
subclasses conforming to protocols with the following syntax:

UIViewController<UITableViewDataSource, UITableViewDelegate>* existential;

We propose to provide similar expressive power to Swift, which will also
improve the bridging of those types from Objective-C.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#proposed-solution>Proposed
solution

The proposal keeps the existing & syntax but allows the first element,
and only the first, to be of class type. The equivalent declaration to the
above Objective-C declaration would look like this:

let existential: UIViewController & UITableViewDataSource & UITableViewDelegate

As in Objective-C, this existential represents classes which have
UIViewController in their parent inheritance hierarchy and which also
conform to the UITableViewDataSource and UITableViewDelegate protocols.

As only the first element in the existential composition syntax can be a
class type, and by extending this rule to typealias expansions, we can make
sure that we only need to read the first element to know if it contains a
class requirement. As a consequence, here is a list of valid and invalid
code and the reasons for them:

let a: Hashable & CustomStringConvertible// VALID: This is still valid, as beforelet b: MyObject & Hashable// VALID: This is the new rule which allows an object type in first positionlet c: CustomStringConvertible & MyObject// INVALID: MyObject is not allowed in second position. A fix-it should help transform it to:// let c: MyObject & CustomStringConvertibletypealias MyObjectStringConvertible = MyObject & CustomStringConvertiblelet d: Hashable & MyObjectStringConvertible// INVALID: The typealias expansion means that the type of d expands to Hashable & MyObject & CustomStringConvertible, which has the class in the wrong position. A fix-it should help transform it to:// let d: MyObjectStringConvertible & Hashabletypealias MyObjectStringConvertible = MyObject & CustomStringConvertiblelet e: MyOtherObject & MyObjectStringConvertible// INVALID: The typealias expansion would allow an existential with two class requirements, which is invalid

The following examples could technically be legal, but we believe we
should keep them invalid to keep the rules simple:

let a: MyObject & MyObject & CustomStringConvertible// This is equivalent to MyObject & CustomStringConvertiblelet b: MyObjectSubclass & MyObject & Hashable// This is equivalent to MyObjectSubclass & Hashabletypealias MyObjectStringConvertible = MyObject & CustomStringConvertiblelet d: MyObject & MyObjectStringConvertible// This is equivalent to MyObject & CustomStringConvertible

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#source-compatibility>Source
compatibility

This is a source breaking change. All types bridged from Objective-C which
use the equivalent Objective-C feature import without the protocol
conformances in Swift 3. This change would increase the existential's
requirement and break on code which does not meet the new protocol
requirements. For example, the following Objective-C code:

@interface MyViewController
- (void)setup:(nonnull UIViewController<UITableViewDataSource,UITableViewDelegate>*)tableViewController;@end

is imported into Swift 3 as:

class MyViewController {
    func setup(tableViewController: UIViewController) {}
}

which allows calling the function with an invalid parameter:

let myViewController: MyViewController()
myViewController.setup(UIViewController())

The previous code would have worked as long as the Objective-C code did
not call any method of UITableViewDataSource or UITableViewDelegate. But
if this proposal is accepted and implemented as-is, the Objective-C code
would now be imported as:

class MyViewController {
    func setup(tableViewController: UIViewController & UITableViewDataSource & UITableViewDelegate) {}
}

That would then cause the Swift code to fail to compile with an error
which states that UIViewController does not conform to the
UITableViewDataSource and UITableViewDelegate protocols.

It is a source-breaking change, but should have a minimal impact for the
following reasons:

   - Not many Objective-C code used the existential syntax in practice.
   - There generated errors are a good thing because they point out
   potential crashes which would have gone un-noticed.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#alternatives-considered>Alternatives
considered

None.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#acknowledgements>
Acknowledgements
Thanks to Austin Zheng <http://github.com/austinzheng> and Matthew Johnson
<https://github.com/anandabits> who brought a lot of attention to
existentials in this mailing-list and from whom most of the ideas in the
proposal come from.

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


(Goffredo Marocchi) #8

+1 on the proposal, but this shows why mixing classes and protocols like so just confuses things...

classOrStructOrThingyOhIMissYouNSProxy <Protocol1, Protocol2, ... , Generic T type, ...>

Gave the separation we needed, but then withGenerics this becomes a bit worse to handle...

···

Sent from my iPhone

On 29 Jan 2017, at 17:03, Kevin Nattinger via swift-evolution <swift-evolution@swift.org> wrote:

+1 on including concrete types, this is currently a gaping hole in the language.
-100 on requiring the concrete type to be first. If I want to specify the types alphabetically, grouped by module, or any other way I choose, why should I not be allowed to do so? The compiler can warn on redundancies and error on contradictions, that’s all we really need.

On Jan 29, 2017, at 8:39 AM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

Hello,

As promised, I wrote the first draft of a proposal to add class requirements to the existential syntax. Please let me know what you think.

https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md

Regards,
David.

Existentials for classes conforming to protocols
Proposal: SE-XXXX
Authors: David Hart, Austin Zheng
Review Manager: TBD
Status: TBD
Introduction

This proposal brings more expressive power to the type system by allowing Swift to represent existentials of classes and subclasses which conform to protocols.

Motivation

Currently, the only existentials which can be represented in Swift are conformances to a set of protocols, using the &syntax:

let existential: Hashable & CustomStringConvertible
On the other hand, Objective-C is capable of expressing existentials of subclasses conforming to protocols with the following syntax:

UIViewController<UITableViewDataSource, UITableViewDelegate>* existential;
We propose to provide similar expressive power to Swift, which will also improve the bridging of those types from Objective-C.

Proposed solution

The proposal keeps the existing & syntax but allows the first element, and only the first, to be of class type. The equivalent declaration to the above Objective-C declaration would look like this:

let existential: UIViewController & UITableViewDataSource & UITableViewDelegate
As in Objective-C, this existential represents classes which have UIViewController in their parent inheritance hierarchy and which also conform to the UITableViewDataSource and UITableViewDelegate protocols.

As only the first element in the existential composition syntax can be a class type, and by extending this rule to typealias expansions, we can make sure that we only need to read the first element to know if it contains a class requirement. As a consequence, here is a list of valid and invalid code and the reasons for them:

let a: Hashable & CustomStringConvertible
// VALID: This is still valid, as before

let b: MyObject & Hashable
// VALID: This is the new rule which allows an object type in first position

let c: CustomStringConvertible & MyObject
// INVALID: MyObject is not allowed in second position. A fix-it should help transform it to:
// let c: MyObject & CustomStringConvertible

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: Hashable & MyObjectStringConvertible
// INVALID: The typealias expansion means that the type of d expands to Hashable & MyObject & CustomStringConvertible, which has the class in the wrong position. A fix-it should help transform it to:
// let d: MyObjectStringConvertible & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let e: MyOtherObject & MyObjectStringConvertible
// INVALID: The typealias expansion would allow an existential with two class requirements, which is invalid
The following examples could technically be legal, but we believe we should keep them invalid to keep the rules simple:

let a: MyObject & MyObject & CustomStringConvertible
// This is equivalent to MyObject & CustomStringConvertible

let b: MyObjectSubclass & MyObject & Hashable
// This is equivalent to MyObjectSubclass & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: MyObject & MyObjectStringConvertible
// This is equivalent to MyObject & CustomStringConvertible
Source compatibility

This is a source breaking change. All types bridged from Objective-C which use the equivalent Objective-C feature import without the protocol conformances in Swift 3. This change would increase the existential's requirement and break on code which does not meet the new protocol requirements. For example, the following Objective-C code:

@interface MyViewController
- (void)setup:(nonnull UIViewController<UITableViewDataSource,UITableViewDelegate>*)tableViewController;
@end
is imported into Swift 3 as:

class MyViewController {
    func setup(tableViewController: UIViewController) {}
}
which allows calling the function with an invalid parameter:

let myViewController: MyViewController()
myViewController.setup(UIViewController())
The previous code would have worked as long as the Objective-C code did not call any method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code would now be imported as:

class MyViewController {
    func setup(tableViewController: UIViewController & UITableViewDataSource & UITableViewDelegate) {}
}
That would then cause the Swift code to fail to compile with an error which states that UIViewController does not conform to the UITableViewDataSource and UITableViewDelegate protocols.

It is a source-breaking change, but should have a minimal impact for the following reasons:

Not many Objective-C code used the existential syntax in practice.
There generated errors are a good thing because they point out potential crashes which would have gone un-noticed.
Alternatives considered

None.

Acknowledgements

Thanks to Austin Zheng and Matthew Johnson who brought a lot of attention to existentials in this mailing-list and from whom most of the ideas in the proposal come from.
_______________________________________________
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


(Slava Pestov) #9

This is a nice generalization of the existing protocol composition syntax. Implementation might get a little tricky — this touches a lot of things, such as inheritance clauses, constraints in generic signatures, and casts. It would require thorough testing.

There are also a few odd corner cases to sort out:

typealias T = SomeClass & SomeProtocol

class C : T { // should we allow this? probably yes
}

protocol P : T { // should we allow this? probably no
}

Slava

···

On Jan 29, 2017, at 8:39 AM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

Hello,

As promised, I wrote the first draft of a proposal to add class requirements to the existential syntax. Please let me know what you think.

https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md

Regards,
David.

Existentials for classes conforming to protocols

Proposal: SE-XXXX <https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md>
Authors: David Hart <http://github.com/hartbit/>, Austin Zheng <http://github.com/austinzheng>
Review Manager: TBD
Status: TBD
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#introduction>Introduction

This proposal brings more expressive power to the type system by allowing Swift to represent existentials of classes and subclasses which conform to protocols.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#motivation>Motivation

Currently, the only existentials which can be represented in Swift are conformances to a set of protocols, using the &syntax:

let existential: Hashable & CustomStringConvertible
On the other hand, Objective-C is capable of expressing existentials of subclasses conforming to protocols with the following syntax:

UIViewController<UITableViewDataSource, UITableViewDelegate>* existential;
We propose to provide similar expressive power to Swift, which will also improve the bridging of those types from Objective-C.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#proposed-solution>Proposed solution

The proposal keeps the existing & syntax but allows the first element, and only the first, to be of class type. The equivalent declaration to the above Objective-C declaration would look like this:

let existential: UIViewController & UITableViewDataSource & UITableViewDelegate
As in Objective-C, this existential represents classes which have UIViewController in their parent inheritance hierarchy and which also conform to the UITableViewDataSource and UITableViewDelegate protocols.

As only the first element in the existential composition syntax can be a class type, and by extending this rule to typealias expansions, we can make sure that we only need to read the first element to know if it contains a class requirement. As a consequence, here is a list of valid and invalid code and the reasons for them:

let a: Hashable & CustomStringConvertible
// VALID: This is still valid, as before

let b: MyObject & Hashable
// VALID: This is the new rule which allows an object type in first position

let c: CustomStringConvertible & MyObject
// INVALID: MyObject is not allowed in second position. A fix-it should help transform it to:
// let c: MyObject & CustomStringConvertible

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: Hashable & MyObjectStringConvertible
// INVALID: The typealias expansion means that the type of d expands to Hashable & MyObject & CustomStringConvertible, which has the class in the wrong position. A fix-it should help transform it to:
// let d: MyObjectStringConvertible & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let e: MyOtherObject & MyObjectStringConvertible
// INVALID: The typealias expansion would allow an existential with two class requirements, which is invalid
The following examples could technically be legal, but we believe we should keep them invalid to keep the rules simple:

let a: MyObject & MyObject & CustomStringConvertible
// This is equivalent to MyObject & CustomStringConvertible

let b: MyObjectSubclass & MyObject & Hashable
// This is equivalent to MyObjectSubclass & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: MyObject & MyObjectStringConvertible
// This is equivalent to MyObject & CustomStringConvertible
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#source-compatibility>Source compatibility

This is a source breaking change. All types bridged from Objective-C which use the equivalent Objective-C feature import without the protocol conformances in Swift 3. This change would increase the existential's requirement and break on code which does not meet the new protocol requirements. For example, the following Objective-C code:

@interface MyViewController
- (void)setup:(nonnull UIViewController<UITableViewDataSource,UITableViewDelegate>*)tableViewController;
@end
is imported into Swift 3 as:

class MyViewController {
    func setup(tableViewController: UIViewController) {}
}
which allows calling the function with an invalid parameter:

let myViewController: MyViewController()
myViewController.setup(UIViewController())
The previous code would have worked as long as the Objective-C code did not call any method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code would now be imported as:

class MyViewController {
    func setup(tableViewController: UIViewController & UITableViewDataSource & UITableViewDelegate) {}
}
That would then cause the Swift code to fail to compile with an error which states that UIViewController does not conform to the UITableViewDataSource and UITableViewDelegate protocols.

It is a source-breaking change, but should have a minimal impact for the following reasons:

Not many Objective-C code used the existential syntax in practice.
There generated errors are a good thing because they point out potential crashes which would have gone un-noticed.
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#alternatives-considered>Alternatives considered

None.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#acknowledgements>Acknowledgements

Thanks to Austin Zheng <http://github.com/austinzheng> and Matthew Johnson <https://github.com/anandabits> who brought a lot of attention to existentials in this mailing-list and from whom most of the ideas in the proposal come from.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Hart) #10

Hi Matthew,

I’ll reply to this post, because it allows me to discuss a few of the points in the discussion, but I’ve read the whole discussion.

Hi David,

This looks like a great start.

One thing we should consider is whether we tie this proposal so tightly to classes or whether it might be better to call these supertype constraints. The feature might also be useful for value types if / when Swift gets value subtyping.

This makes sense, especially with the Substring : String discussions going on. When I rework the proposal, I’ll try to make it more general.

One enhancement that might be worth considering. Specifically, allowing protocols to declare a specific supertype requirement in the place where a `class` constraint would usually be found. After this proposal we could already do something similar using a `class` constraint to define a protocol and a typealias to bind it to the supertype requirement. It seems like allowing users to state this more directly would be a good idea.

You lost me there. Can you give examples?

As only the first element in the existential composition syntax can be a class type, and by extending this rule to typealias expansions, we can make sure that we only need to read the first element to know if it contains a class requirement.

I think this is unnecessarily limiting in a couple of ways. I agree that a class should come first if it is mentioned explicitly***. I am less sure we should require this when the type is part of a typealias combined with other protocol requirements.

I agree with Chris that I think it’s important to require the class be mentioned first when the class is mentioned explicitly. Even if we lost the Any<Base, Protocol> syntax, there is still enough similarity to a class’s inheritance/conformance clause to keep the consistency and readability.

For example, one use case I remember discussing with Austin is refining supertype requirements. If I have a typealias which requires a superclass `Base` I should be able to form an existential using that typealias that *refines* that requirement to some type *Derived* which is a non-final subtype of `Base`. This would require syntax that allows us to put a class name in the first position, but also mention a typealias with a supertype requirement in a subsequent position.

I’ve read the examples in the thread and I think I agree that those cases should be accepted. But just to make sure we are on the same page, what does everyone think of the validity of the following cases? For shorthand, I use parentheses to represent typealias expansion. For example, when I write:

Protocol1 & (Protocol2 & Protocol3)
I mean:

typealias Something = Protocol2 & Protocol3
Protocol1 & Something
Questions

Should class requirements be fixed to first position? I.e., should Protocol & Base be valid and equivalent to Base & Protocol?
Should repetition of class requirements in the same declaration be allowed? I.e., should Base & Base be valid and equivalent to Base?
Should repetition of class requirements through typealias expansion be allowed? I.e., should Base & (Base & Protocol) be valid and equivalent to Base & Protocol?
Should type and sub-type requirements in the same declaration be allowed? I.e., should Base & Derived or Derived & Base be valid and equivalent to Derived?
Should type and sub-type requirements through typealias expansion be allowed? I.e., should Base & (Derived & Protocol) or Derived & (Base & Protocol) be valid and equivalent to Derived & Protocol?
My Answers

No, for the reasons stated above.
No, because it doesn’t make sense to repeat it in the same declaration.
Yes, I’m gonna start agreeing with you and think will ease typealias composition.
No, for the same reasons as 2.
Yes, for the same reasons as 3.
David.

···

On 29 Jan 2017, at 18:30, Matthew Johnson <matthew@anandabits.com> wrote:

Matthew

*** One argument against requiring a class to come first is that we could conceptualize `&` as a type operator with a handful of overloads. This would include both lhs and rhs are “protocol only kinds” as well as overloads with a “protocol only kind” in either lhs or rhs and a “supertype kind” in the other position. The tricky part of pulling this off would be including an overload where both lhs and rhs have a “supertype kind”, but only when the operands have a subtype / supertype relationship with each other.

I suspect this conceptualization isn’t worth the complexity it brings, but it is tempting to try and view `&` as a type operator. As long as this only involves a loosening of restrictions it could probably be introduced as an additive change down the road.

On Jan 29, 2017, at 10:39 AM, David Hart <david@hartbit.com <mailto:david@hartbit.com>> wrote:

Hello,

As promised, I wrote the first draft of a proposal to add class requirements to the existential syntax. Please let me know what you think.

https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md

Regards,
David.

Existentials for classes conforming to protocols

Proposal: SE-XXXX <https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md>
Authors: David Hart <http://github.com/hartbit/>, Austin Zheng <http://github.com/austinzheng>
Review Manager: TBD
Status: TBD
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#introduction>Introduction

This proposal brings more expressive power to the type system by allowing Swift to represent existentials of classes and subclasses which conform to protocols.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#motivation>Motivation

Currently, the only existentials which can be represented in Swift are conformances to a set of protocols, using the &syntax:

let existential: Hashable & CustomStringConvertible
On the other hand, Objective-C is capable of expressing existentials of subclasses conforming to protocols with the following syntax:

UIViewController<UITableViewDataSource, UITableViewDelegate>* existential;
We propose to provide similar expressive power to Swift, which will also improve the bridging of those types from Objective-C.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#proposed-solution>Proposed solution

The proposal keeps the existing & syntax but allows the first element, and only the first, to be of class type. The equivalent declaration to the above Objective-C declaration would look like this:

let existential: UIViewController & UITableViewDataSource & UITableViewDelegate
As in Objective-C, this existential represents classes which have UIViewController in their parent inheritance hierarchy and which also conform to the UITableViewDataSource and UITableViewDelegate protocols.

As only the first element in the existential composition syntax can be a class type, and by extending this rule to typealias expansions, we can make sure that we only need to read the first element to know if it contains a class requirement. As a consequence, here is a list of valid and invalid code and the reasons for them:

let a: Hashable & CustomStringConvertible
// VALID: This is still valid, as before

let b: MyObject & Hashable
// VALID: This is the new rule which allows an object type in first position

let c: CustomStringConvertible & MyObject
// INVALID: MyObject is not allowed in second position. A fix-it should help transform it to:
// let c: MyObject & CustomStringConvertible

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: Hashable & MyObjectStringConvertible
// INVALID: The typealias expansion means that the type of d expands to Hashable & MyObject & CustomStringConvertible, which has the class in the wrong position. A fix-it should help transform it to:
// let d: MyObjectStringConvertible & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let e: MyOtherObject & MyObjectStringConvertible
// INVALID: The typealias expansion would allow an existential with two class requirements, which is invalid
The following examples could technically be legal, but we believe we should keep them invalid to keep the rules simple:

let a: MyObject & MyObject & CustomStringConvertible
// This is equivalent to MyObject & CustomStringConvertible

let b: MyObjectSubclass & MyObject & Hashable
// This is equivalent to MyObjectSubclass & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: MyObject & MyObjectStringConvertible
// This is equivalent to MyObject & CustomStringConvertible
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#source-compatibility>Source compatibility

This is a source breaking change. All types bridged from Objective-C which use the equivalent Objective-C feature import without the protocol conformances in Swift 3. This change would increase the existential's requirement and break on code which does not meet the new protocol requirements. For example, the following Objective-C code:

@interface MyViewController
- (void)setup:(nonnull UIViewController<UITableViewDataSource,UITableViewDelegate>*)tableViewController;
@end
is imported into Swift 3 as:

class MyViewController {
    func setup(tableViewController: UIViewController) {}
}
which allows calling the function with an invalid parameter:

let myViewController: MyViewController()
myViewController.setup(UIViewController())
The previous code would have worked as long as the Objective-C code did not call any method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code would now be imported as:

class MyViewController {
    func setup(tableViewController: UIViewController & UITableViewDataSource & UITableViewDelegate) {}
}
That would then cause the Swift code to fail to compile with an error which states that UIViewController does not conform to the UITableViewDataSource and UITableViewDelegate protocols.

It is a source-breaking change, but should have a minimal impact for the following reasons:

Not many Objective-C code used the existential syntax in practice.
There generated errors are a good thing because they point out potential crashes which would have gone un-noticed.
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#alternatives-considered>Alternatives considered

None.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#acknowledgements>Acknowledgements

Thanks to Austin Zheng <http://github.com/austinzheng> and Matthew Johnson <https://github.com/anandabits> who brought a lot of attention to existentials in this mailing-list and from whom most of the ideas in the proposal come from.


(Douglas Gregor) #11

Hello,

As promised, I wrote the first draft of a proposal to add class requirements to the existential syntax. Please let me know what you think.

https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md

This looks good! I’m looking forward to the second draft, but I have one question.

Did you consider the generalized “class” constraint? IIRC, this was in Austin’s larger proposal, and it allowed for (e.g.)

  typealias CustomStringConvertibleClass = class & CustomStringConvertible // class that conforms to CustomStringConvertible

and potentially a wonderful cleanup where AnyObject ceases to be a weird special protocol and instead becomes

  typealias AnyObject = Any & class

  - Doug

···

On Jan 29, 2017, at 8:39 AM, David Hart <david@hartbit.com> wrote:

Regards,
David.

Existentials for classes conforming to protocols

Proposal: SE-XXXX <https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md>
Authors: David Hart <http://github.com/hartbit/>, Austin Zheng <http://github.com/austinzheng>
Review Manager: TBD
Status: TBD
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#introduction>Introduction

This proposal brings more expressive power to the type system by allowing Swift to represent existentials of classes and subclasses which conform to protocols.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#motivation>Motivation

Currently, the only existentials which can be represented in Swift are conformances to a set of protocols, using the &syntax:

let existential: Hashable & CustomStringConvertible
On the other hand, Objective-C is capable of expressing existentials of subclasses conforming to protocols with the following syntax:

UIViewController<UITableViewDataSource, UITableViewDelegate>* existential;
We propose to provide similar expressive power to Swift, which will also improve the bridging of those types from Objective-C.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#proposed-solution>Proposed solution

The proposal keeps the existing & syntax but allows the first element, and only the first, to be of class type. The equivalent declaration to the above Objective-C declaration would look like this:

let existential: UIViewController & UITableViewDataSource & UITableViewDelegate
As in Objective-C, this existential represents classes which have UIViewController in their parent inheritance hierarchy and which also conform to the UITableViewDataSource and UITableViewDelegate protocols.

As only the first element in the existential composition syntax can be a class type, and by extending this rule to typealias expansions, we can make sure that we only need to read the first element to know if it contains a class requirement. As a consequence, here is a list of valid and invalid code and the reasons for them:

let a: Hashable & CustomStringConvertible
// VALID: This is still valid, as before

let b: MyObject & Hashable
// VALID: This is the new rule which allows an object type in first position

let c: CustomStringConvertible & MyObject
// INVALID: MyObject is not allowed in second position. A fix-it should help transform it to:
// let c: MyObject & CustomStringConvertible

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: Hashable & MyObjectStringConvertible
// INVALID: The typealias expansion means that the type of d expands to Hashable & MyObject & CustomStringConvertible, which has the class in the wrong position. A fix-it should help transform it to:
// let d: MyObjectStringConvertible & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let e: MyOtherObject & MyObjectStringConvertible
// INVALID: The typealias expansion would allow an existential with two class requirements, which is invalid
The following examples could technically be legal, but we believe we should keep them invalid to keep the rules simple:

let a: MyObject & MyObject & CustomStringConvertible
// This is equivalent to MyObject & CustomStringConvertible

let b: MyObjectSubclass & MyObject & Hashable
// This is equivalent to MyObjectSubclass & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: MyObject & MyObjectStringConvertible
// This is equivalent to MyObject & CustomStringConvertible
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#source-compatibility>Source compatibility

This is a source breaking change. All types bridged from Objective-C which use the equivalent Objective-C feature import without the protocol conformances in Swift 3. This change would increase the existential's requirement and break on code which does not meet the new protocol requirements. For example, the following Objective-C code:

@interface MyViewController
- (void)setup:(nonnull UIViewController<UITableViewDataSource,UITableViewDelegate>*)tableViewController;
@end
is imported into Swift 3 as:

class MyViewController {
    func setup(tableViewController: UIViewController) {}
}
which allows calling the function with an invalid parameter:

let myViewController: MyViewController()
myViewController.setup(UIViewController())
The previous code would have worked as long as the Objective-C code did not call any method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code would now be imported as:

class MyViewController {
    func setup(tableViewController: UIViewController & UITableViewDataSource & UITableViewDelegate) {}
}
That would then cause the Swift code to fail to compile with an error which states that UIViewController does not conform to the UITableViewDataSource and UITableViewDelegate protocols.

It is a source-breaking change, but should have a minimal impact for the following reasons:

Not many Objective-C code used the existential syntax in practice.
There generated errors are a good thing because they point out potential crashes which would have gone un-noticed.
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#alternatives-considered>Alternatives considered

None.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#acknowledgements>Acknowledgements

Thanks to Austin Zheng <http://github.com/austinzheng> and Matthew Johnson <https://github.com/anandabits> who brought a lot of attention to existentials in this mailing-list and from whom most of the ideas in the proposal come from.


(Jordan Rose) #12

I agree with Chris on both counts. Either we need to continue to import ‘NSFoo <NSBar> *’ as ‘NSFoo’ in the Swift 4 compiler’s “Swift 3 compatibility mode”, or we need to come up with all the cases where a normally-unsound conversion needs to be permitted (possibly with a warning) in order to avoid breaking existing programs. The latter is technically doable, but would be much more work, which would likely hurt the proposal’s chances of getting into Swift 4.

Thank you for writing this up, David!

Jordan

···

On Jan 29, 2017, at 12:17, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

+1 on the proposal overall. You might want to clarify the writing wrt source compatibility though. The new syntax and type system mechanics should work in Swift 3 mode, it is just the importer change that must be conditionalized.

-Chris

On Jan 29, 2017, at 8:39 AM, David Hart via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hello,

As promised, I wrote the first draft of a proposal to add class requirements to the existential syntax. Please let me know what you think.

https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md

Regards,
David.

Existentials for classes conforming to protocols

Proposal: SE-XXXX <https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md>
Authors: David Hart <http://github.com/hartbit/>, Austin Zheng <http://github.com/austinzheng>
Review Manager: TBD
Status: TBD
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#introduction>Introduction

This proposal brings more expressive power to the type system by allowing Swift to represent existentials of classes and subclasses which conform to protocols.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#motivation>Motivation

Currently, the only existentials which can be represented in Swift are conformances to a set of protocols, using the &syntax:

let existential: Hashable & CustomStringConvertible
On the other hand, Objective-C is capable of expressing existentials of subclasses conforming to protocols with the following syntax:

UIViewController<UITableViewDataSource, UITableViewDelegate>* existential;
We propose to provide similar expressive power to Swift, which will also improve the bridging of those types from Objective-C.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#proposed-solution>Proposed solution

The proposal keeps the existing & syntax but allows the first element, and only the first, to be of class type. The equivalent declaration to the above Objective-C declaration would look like this:

let existential: UIViewController & UITableViewDataSource & UITableViewDelegate
As in Objective-C, this existential represents classes which have UIViewController in their parent inheritance hierarchy and which also conform to the UITableViewDataSource and UITableViewDelegate protocols.

As only the first element in the existential composition syntax can be a class type, and by extending this rule to typealias expansions, we can make sure that we only need to read the first element to know if it contains a class requirement. As a consequence, here is a list of valid and invalid code and the reasons for them:

let a: Hashable & CustomStringConvertible
// VALID: This is still valid, as before

let b: MyObject & Hashable
// VALID: This is the new rule which allows an object type in first position

let c: CustomStringConvertible & MyObject
// INVALID: MyObject is not allowed in second position. A fix-it should help transform it to:
// let c: MyObject & CustomStringConvertible

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: Hashable & MyObjectStringConvertible
// INVALID: The typealias expansion means that the type of d expands to Hashable & MyObject & CustomStringConvertible, which has the class in the wrong position. A fix-it should help transform it to:
// let d: MyObjectStringConvertible & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let e: MyOtherObject & MyObjectStringConvertible
// INVALID: The typealias expansion would allow an existential with two class requirements, which is invalid
The following examples could technically be legal, but we believe we should keep them invalid to keep the rules simple:

let a: MyObject & MyObject & CustomStringConvertible
// This is equivalent to MyObject & CustomStringConvertible

let b: MyObjectSubclass & MyObject & Hashable
// This is equivalent to MyObjectSubclass & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: MyObject & MyObjectStringConvertible
// This is equivalent to MyObject & CustomStringConvertible
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#source-compatibility>Source compatibility

This is a source breaking change. All types bridged from Objective-C which use the equivalent Objective-C feature import without the protocol conformances in Swift 3. This change would increase the existential's requirement and break on code which does not meet the new protocol requirements. For example, the following Objective-C code:

@interface MyViewController
- (void)setup:(nonnull UIViewController<UITableViewDataSource,UITableViewDelegate>*)tableViewController;
@end
is imported into Swift 3 as:

class MyViewController {
    func setup(tableViewController: UIViewController) {}
}
which allows calling the function with an invalid parameter:

let myViewController: MyViewController()
myViewController.setup(UIViewController())
The previous code would have worked as long as the Objective-C code did not call any method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code would now be imported as:

class MyViewController {
    func setup(tableViewController: UIViewController & UITableViewDataSource & UITableViewDelegate) {}
}
That would then cause the Swift code to fail to compile with an error which states that UIViewController does not conform to the UITableViewDataSource and UITableViewDelegate protocols.

It is a source-breaking change, but should have a minimal impact for the following reasons:

Not many Objective-C code used the existential syntax in practice.
There generated errors are a good thing because they point out potential crashes which would have gone un-noticed.
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#alternatives-considered>Alternatives considered

None.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#acknowledgements>Acknowledgements

Thanks to Austin Zheng <http://github.com/austinzheng> and Matthew Johnson <https://github.com/anandabits> who brought a lot of attention to existentials in this mailing-list and from whom most of the ideas in the proposal come from.
_______________________________________________
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


(Matt Whiteside) #13

Thanks for writing this proposal David.

···

On Jan 29, 2017, at 10:13, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

As Matthew mentioned, the rules can certainly later be relaxed, but given that this proposal has the compiler generating fix-its for subclasses in second position, is there a reason other than stylistic for demanding MyClass & MyProtocol instead of MyProtocol & MyClass?

From a naive perspective, it seems that if the compiler understands my meaning perfectly, it should just accept that spelling rather than complain.

I had that thought too. Since ‘and’ is a symmetric operation, requiring the class to be in the first position seems counter-intuitive.

-Matt


(Austin Zheng) #14

The "class comes first" requirement made more sense when the proposed syntax was still "Any<T, U, V>", intentionally mirroring how the superclass and conformances are declared on a class declaration (the archives contain more detailed arguments, both pro and con). Now that the syntax is "T & U & V", I agree that privileging the class requirement is counterintuitive and probably unhelpful.

Austin

···

On Jan 29, 2017, at 10:37 AM, Matt Whiteside via swift-evolution <swift-evolution@swift.org> wrote:

Thanks for writing this proposal David.

On Jan 29, 2017, at 10:13, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

As Matthew mentioned, the rules can certainly later be relaxed, but given that this proposal has the compiler generating fix-its for subclasses in second position, is there a reason other than stylistic for demanding MyClass & MyProtocol instead of MyProtocol & MyClass?

From a naive perspective, it seems that if the compiler understands my meaning perfectly, it should just accept that spelling rather than complain.

I had that thought too. Since ‘and’ is a symmetric operation, requiring the class to be in the first position seems counter-intuitive.

-Matt

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


(Xiaodi Wu) #15

Hi Matthew,

I’ll reply to this post, because it allows me to discuss a few of the
points in the discussion, but I’ve read the whole discussion.

Hi David,

This looks like a great start.

One thing we should consider is whether we tie this proposal so tightly to
classes or whether it might be better to call these supertype constraints.
The feature might also be useful for value types if / when Swift gets value
subtyping.

This makes sense, especially with the Substring : String discussions going
on. When I rework the proposal, I’ll try to make it more general.

One enhancement that might be worth considering. Specifically, allowing
protocols to declare a specific supertype requirement in the place where a
`class` constraint would usually be found. After this proposal we could
already do something similar using a `class` constraint to define a
protocol and a typealias to bind it to the supertype requirement. It seems
like allowing users to state this more directly would be a good idea.

You lost me there. Can you give examples?

As only the first element in the existential composition syntax can be a
class type, and by extending this rule to typealias expansions, we can make
sure that we only need to read the first element to know if it contains a
class requirement.

I think this is unnecessarily limiting in a couple of ways. I agree that
a class should come first if it is mentioned explicitly***. I am less sure
we should require this when the type is part of a typealias combined with
other protocol requirements.

I agree with Chris that I think it’s important to require the class be
mentioned first when the class is mentioned explicitly. Even if we lost the
Any<Base, Protocol> syntax, there is still enough similarity to a class’s
inheritance/conformance clause to keep the consistency and readability.

For example, one use case I remember discussing with Austin is refining
supertype requirements. If I have a typealias which requires a superclass
`Base` I should be able to form an existential using that typealias that
*refines* that requirement to some type *Derived* which is a non-final
subtype of `Base`. This would require syntax that allows us to put a class
name in the first position, but also mention a typealias with a supertype
requirement in a subsequent position.

I’ve read the examples in the thread and I think I agree that those cases
should be accepted. But just to make sure we are on the same page, what
does everyone think of the validity of the following cases? For shorthand,
I use parentheses to represent typealias expansion. For example, when I
write:

Protocol1 & (Protocol2 & Protocol3)

I mean:

typealias Something = Protocol2 & Protocol3Protocol1 & Something

*Questions*

   1. Should class requirements be fixed to first position? I.e., should Protocol
   & Base be valid and equivalent to Base & Protocol?
   2. Should repetition of class requirements in the same declaration be
   allowed? I.e., should Base & Base be valid and equivalent to Base?
   3. Should repetition of class requirements through typealias expansion
   be allowed? I.e., should Base & (Base & Protocol) be valid and
   equivalent to Base & Protocol?
   4. Should type and sub-type requirements in the same declaration be
   allowed? I.e., should Base & Derived or Derived & Base be valid and
   equivalent to Derived?
   5. Should type and sub-type requirements through typealias expansion
   be allowed? I.e., should Base & (Derived & Protocol) or Derived &
   (Base & Protocol) be valid and equivalent to Derived & Protocol?

*My Answers*

   1. No, for the reasons stated above.
   2. No, because it doesn’t make sense to repeat it in the same
   declaration.
   3. Yes, I’m gonna start agreeing with you and think will ease
   typealias composition.
   4. No, for the same reasons as 2.
   5. Yes, for the same reasons as 3.

That's a _reasonable_ set of answers if you want to require Base to

precede Protocol *and* you want to ease rules for typealiases. However,
using your notation, should `(Protocol & Protocol) & (Base & Protocol)` be
allowed?

If not, your rules will have to get pretty complicated. OTOH, if so, it
seems like an awfully heavy-handed yet simultaneously ineffective
hardcoding of a style preference, since I'd be able to use `typealias Base_
= Base & Any` to circumvent the rule any time I like.

David.

···

On Sun, Jan 29, 2017 at 4:01 PM, David Hart via swift-evolution < swift-evolution@swift.org> wrote:

On 29 Jan 2017, at 18:30, Matthew Johnson <matthew@anandabits.com> wrote:

Matthew

*** One argument against requiring a class to come first is that we could
conceptualize `&` as a type operator with a handful of overloads. This
would include both lhs and rhs are “protocol only kinds” as well as
overloads with a “protocol only kind” in either lhs or rhs and a “supertype
kind” in the other position. The tricky part of pulling this off would be
including an overload where both lhs and rhs have a “supertype kind”, but
only when the operands have a subtype / supertype relationship with each
other.

I suspect this conceptualization isn’t worth the complexity it brings, but
it is tempting to try and view `&` as a type operator. As long as this
only involves a loosening of restrictions it could probably be introduced
as an additive change down the road.

On Jan 29, 2017, at 10:39 AM, David Hart <david@hartbit.com> wrote:

Hello,

As promised, I wrote the first draft of a proposal to add class
requirements to the existential syntax. Please let me know what you think.

https://github.com/hartbit/swift-evolution/blob/subclass-
existentials/proposals/XXXX-subclass-existentials.md

Regards,
David.

Existentials for classes conforming to protocols

   - Proposal: SE-XXXX
   <https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md>
   - Authors: David Hart <http://github.com/hartbit/>, Austin Zheng
   <http://github.com/austinzheng>
   - Review Manager: TBD
   - Status: TBD

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#introduction>
Introduction

This proposal brings more expressive power to the type system by allowing
Swift to represent existentials of classes and subclasses which conform to
protocols.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#motivation>
Motivation

Currently, the only existentials which can be represented in Swift are
conformances to a set of protocols, using the &syntax:

let existential: Hashable & CustomStringConvertible

On the other hand, Objective-C is capable of expressing existentials of
subclasses conforming to protocols with the following syntax:

UIViewController<UITableViewDataSource, UITableViewDelegate>* existential;

We propose to provide similar expressive power to Swift, which will also
improve the bridging of those types from Objective-C.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#proposed-solution>Proposed
solution

The proposal keeps the existing & syntax but allows the first element,
and only the first, to be of class type. The equivalent declaration to the
above Objective-C declaration would look like this:

let existential: UIViewController & UITableViewDataSource & UITableViewDelegate

As in Objective-C, this existential represents classes which have
UIViewController in their parent inheritance hierarchy and which also
conform to the UITableViewDataSource and UITableViewDelegate protocols.

As only the first element in the existential composition syntax can be a
class type, and by extending this rule to typealias expansions, we can make
sure that we only need to read the first element to know if it contains a
class requirement. As a consequence, here is a list of valid and invalid
code and the reasons for them:

let a: Hashable & CustomStringConvertible// VALID: This is still valid, as beforelet b: MyObject & Hashable// VALID: This is the new rule which allows an object type in first positionlet c: CustomStringConvertible & MyObject// INVALID: MyObject is not allowed in second position. A fix-it should help transform it to:// let c: MyObject & CustomStringConvertibletypealias MyObjectStringConvertible = MyObject & CustomStringConvertiblelet d: Hashable & MyObjectStringConvertible// INVALID: The typealias expansion means that the type of d expands to Hashable & MyObject & CustomStringConvertible, which has the class in the wrong position. A fix-it should help transform it to:// let d: MyObjectStringConvertible & Hashabletypealias MyObjectStringConvertible = MyObject & CustomStringConvertiblelet e: MyOtherObject & MyObjectStringConvertible// INVALID: The typealias expansion would allow an existential with two class requirements, which is invalid

The following examples could technically be legal, but we believe we
should keep them invalid to keep the rules simple:

let a: MyObject & MyObject & CustomStringConvertible// This is equivalent to MyObject & CustomStringConvertiblelet b: MyObjectSubclass & MyObject & Hashable// This is equivalent to MyObjectSubclass & Hashabletypealias MyObjectStringConvertible = MyObject & CustomStringConvertiblelet d: MyObject & MyObjectStringConvertible// This is equivalent to MyObject & CustomStringConvertible

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#source-compatibility>Source
compatibility

This is a source breaking change. All types bridged from Objective-C which
use the equivalent Objective-C feature import without the protocol
conformances in Swift 3. This change would increase the existential's
requirement and break on code which does not meet the new protocol
requirements. For example, the following Objective-C code:

@interface MyViewController
- (void)setup:(nonnull UIViewController<UITableViewDataSource,UITableViewDelegate>*)tableViewController;@end

is imported into Swift 3 as:

class MyViewController {
    func setup(tableViewController: UIViewController) {}
}

which allows calling the function with an invalid parameter:

let myViewController: MyViewController()
myViewController.setup(UIViewController())

The previous code would have worked as long as the Objective-C code did
not call any method of UITableViewDataSource or UITableViewDelegate. But
if this proposal is accepted and implemented as-is, the Objective-C code
would now be imported as:

class MyViewController {
    func setup(tableViewController: UIViewController & UITableViewDataSource & UITableViewDelegate) {}
}

That would then cause the Swift code to fail to compile with an error
which states that UIViewController does not conform to the
UITableViewDataSource and UITableViewDelegate protocols.

It is a source-breaking change, but should have a minimal impact for the
following reasons:

   - Not many Objective-C code used the existential syntax in practice.
   - There generated errors are a good thing because they point out
   potential crashes which would have gone un-noticed.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#alternatives-considered>Alternatives
considered

None.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#acknowledgements>
Acknowledgements
Thanks to Austin Zheng <http://github.com/austinzheng> and Matthew Johnson
<https://github.com/anandabits> who brought a lot of attention to
existentials in this mailing-list and from whom most of the ideas in the
proposal come from.

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


(Matthew Johnson) #16

Hi Matthew,

I’ll reply to this post, because it allows me to discuss a few of the points in the discussion, but I’ve read the whole discussion.

Hi David,

This looks like a great start.

One thing we should consider is whether we tie this proposal so tightly to classes or whether it might be better to call these supertype constraints. The feature might also be useful for value types if / when Swift gets value subtyping.

This makes sense, especially with the Substring : String discussions going on. When I rework the proposal, I’ll try to make it more general.

One enhancement that might be worth considering. Specifically, allowing protocols to declare a specific supertype requirement in the place where a `class` constraint would usually be found. After this proposal we could already do something similar using a `class` constraint to define a protocol and a typealias to bind it to the supertype requirement. It seems like allowing users to state this more directly would be a good idea.

You lost me there. Can you give examples?

Sure. In an iOS app you might want to create a protocol that should only be conformed to by `UIViewController` subclasses:

protocol TrackedViewController: UIViewController {
    var analyticsTitle: String { get }
}

This is roughly equivalent to under your proposal:

protocol TrackedObject: class {
    var analyticsTitle: String { get }
}
typealias TrackedViewController = UIViewController & TrackedObject

But in this case we would be able to avoid the TrackedObject protocol which we didn’t care about or want, but were required to create in the process of creating our typealias.

As only the first element in the existential composition syntax can be a class type, and by extending this rule to typealias expansions, we can make sure that we only need to read the first element to know if it contains a class requirement.

I think this is unnecessarily limiting in a couple of ways. I agree that a class should come first if it is mentioned explicitly***. I am less sure we should require this when the type is part of a typealias combined with other protocol requirements.

I agree with Chris that I think it’s important to require the class be mentioned first when the class is mentioned explicitly. Even if we lost the Any<Base, Protocol> syntax, there is still enough similarity to a class’s inheritance/conformance clause to keep the consistency and readability.

For example, one use case I remember discussing with Austin is refining supertype requirements. If I have a typealias which requires a superclass `Base` I should be able to form an existential using that typealias that *refines* that requirement to some type *Derived* which is a non-final subtype of `Base`. This would require syntax that allows us to put a class name in the first position, but also mention a typealias with a supertype requirement in a subsequent position.

I’ve read the examples in the thread and I think I agree that those cases should be accepted. But just to make sure we are on the same page, what does everyone think of the validity of the following cases? For shorthand, I use parentheses to represent typealias expansion. For example, when I write:

Protocol1 & (Protocol2 & Protocol3)
I mean:

typealias Something = Protocol2 & Protocol3
Protocol1 & Something
Questions

Should class requirements be fixed to first position? I.e., should Protocol & Base be valid and equivalent to Base & Protocol?
Should repetition of class requirements in the same declaration be allowed? I.e., should Base & Base be valid and equivalent to Base?
Should repetition of class requirements through typealias expansion be allowed? I.e., should Base & (Base & Protocol) be valid and equivalent to Base & Protocol?
Should type and sub-type requirements in the same declaration be allowed? I.e., should Base & Derived or Derived & Base be valid and equivalent to Derived?
Should type and sub-type requirements through typealias expansion be allowed? I.e., should Base & (Derived & Protocol) or Derived & (Base & Protocol) be valid and equivalent to Derived & Protocol?
My Answers

No, for the reasons stated above.
No, because it doesn’t make sense to repeat it in the same declaration.
Yes, I’m gonna start agreeing with you and think will ease typealias composition.
No, for the same reasons as 2.
Yes, for the same reasons as 3.

My answer depends on whether we adopt the perspective of intersection types and allow things like `Base1 & Base2` or not. If we’re not going to do that (at least not yet) I think your answers are the right ones.

However, if we decide to allow uninhabiatable existentials like`Base1 & Base2` I think it would be best to just relax the rules such that they follow the logic of intersection types.

The good news is that (as far as I can tell) it would be an additive change to start with your rules and later adopt the intersection types approach.

···

On Jan 29, 2017, at 4:01 PM, David Hart <david@hartbit.com> wrote:

On 29 Jan 2017, at 18:30, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

David.

Matthew

*** One argument against requiring a class to come first is that we could conceptualize `&` as a type operator with a handful of overloads. This would include both lhs and rhs are “protocol only kinds” as well as overloads with a “protocol only kind” in either lhs or rhs and a “supertype kind” in the other position. The tricky part of pulling this off would be including an overload where both lhs and rhs have a “supertype kind”, but only when the operands have a subtype / supertype relationship with each other.

I suspect this conceptualization isn’t worth the complexity it brings, but it is tempting to try and view `&` as a type operator. As long as this only involves a loosening of restrictions it could probably be introduced as an additive change down the road.

On Jan 29, 2017, at 10:39 AM, David Hart <david@hartbit.com <mailto:david@hartbit.com>> wrote:

Hello,

As promised, I wrote the first draft of a proposal to add class requirements to the existential syntax. Please let me know what you think.

https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md

Regards,
David.

Existentials for classes conforming to protocols

Proposal: SE-XXXX <https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md>
Authors: David Hart <http://github.com/hartbit/>, Austin Zheng <http://github.com/austinzheng>
Review Manager: TBD
Status: TBD
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#introduction>Introduction

This proposal brings more expressive power to the type system by allowing Swift to represent existentials of classes and subclasses which conform to protocols.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#motivation>Motivation

Currently, the only existentials which can be represented in Swift are conformances to a set of protocols, using the &syntax:

let existential: Hashable & CustomStringConvertible
On the other hand, Objective-C is capable of expressing existentials of subclasses conforming to protocols with the following syntax:

UIViewController<UITableViewDataSource, UITableViewDelegate>* existential;
We propose to provide similar expressive power to Swift, which will also improve the bridging of those types from Objective-C.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#proposed-solution>Proposed solution

The proposal keeps the existing & syntax but allows the first element, and only the first, to be of class type. The equivalent declaration to the above Objective-C declaration would look like this:

let existential: UIViewController & UITableViewDataSource & UITableViewDelegate
As in Objective-C, this existential represents classes which have UIViewController in their parent inheritance hierarchy and which also conform to the UITableViewDataSource and UITableViewDelegate protocols.

As only the first element in the existential composition syntax can be a class type, and by extending this rule to typealias expansions, we can make sure that we only need to read the first element to know if it contains a class requirement. As a consequence, here is a list of valid and invalid code and the reasons for them:

let a: Hashable & CustomStringConvertible
// VALID: This is still valid, as before

let b: MyObject & Hashable
// VALID: This is the new rule which allows an object type in first position

let c: CustomStringConvertible & MyObject
// INVALID: MyObject is not allowed in second position. A fix-it should help transform it to:
// let c: MyObject & CustomStringConvertible

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: Hashable & MyObjectStringConvertible
// INVALID: The typealias expansion means that the type of d expands to Hashable & MyObject & CustomStringConvertible, which has the class in the wrong position. A fix-it should help transform it to:
// let d: MyObjectStringConvertible & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let e: MyOtherObject & MyObjectStringConvertible
// INVALID: The typealias expansion would allow an existential with two class requirements, which is invalid
The following examples could technically be legal, but we believe we should keep them invalid to keep the rules simple:

let a: MyObject & MyObject & CustomStringConvertible
// This is equivalent to MyObject & CustomStringConvertible

let b: MyObjectSubclass & MyObject & Hashable
// This is equivalent to MyObjectSubclass & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: MyObject & MyObjectStringConvertible
// This is equivalent to MyObject & CustomStringConvertible
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#source-compatibility>Source compatibility

This is a source breaking change. All types bridged from Objective-C which use the equivalent Objective-C feature import without the protocol conformances in Swift 3. This change would increase the existential's requirement and break on code which does not meet the new protocol requirements. For example, the following Objective-C code:

@interface MyViewController
- (void)setup:(nonnull UIViewController<UITableViewDataSource,UITableViewDelegate>*)tableViewController;
@end
is imported into Swift 3 as:

class MyViewController {
    func setup(tableViewController: UIViewController) {}
}
which allows calling the function with an invalid parameter:

let myViewController: MyViewController()
myViewController.setup(UIViewController())
The previous code would have worked as long as the Objective-C code did not call any method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code would now be imported as:

class MyViewController {
    func setup(tableViewController: UIViewController & UITableViewDataSource & UITableViewDelegate) {}
}
That would then cause the Swift code to fail to compile with an error which states that UIViewController does not conform to the UITableViewDataSource and UITableViewDelegate protocols.

It is a source-breaking change, but should have a minimal impact for the following reasons:

Not many Objective-C code used the existential syntax in practice.
There generated errors are a good thing because they point out potential crashes which would have gone un-noticed.
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#alternatives-considered>Alternatives considered

None.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#acknowledgements>Acknowledgements

Thanks to Austin Zheng <http://github.com/austinzheng> and Matthew Johnson <https://github.com/anandabits> who brought a lot of attention to existentials in this mailing-list and from whom most of the ideas in the proposal come from.


(Xiaodi Wu) #17

Hi Matthew,

I’ll reply to this post, because it allows me to discuss a few of the
points in the discussion, but I’ve read the whole discussion.

Hi David,

This looks like a great start.

One thing we should consider is whether we tie this proposal so tightly to
classes or whether it might be better to call these supertype constraints.
The feature might also be useful for value types if / when Swift gets value
subtyping.

This makes sense, especially with the Substring : String discussions going
on. When I rework the proposal, I’ll try to make it more general.

One enhancement that might be worth considering. Specifically, allowing
protocols to declare a specific supertype requirement in the place where a
`class` constraint would usually be found. After this proposal we could
already do something similar using a `class` constraint to define a
protocol and a typealias to bind it to the supertype requirement. It seems
like allowing users to state this more directly would be a good idea.

You lost me there. Can you give examples?

Sure. In an iOS app you might want to create a protocol that should only
be conformed to by `UIViewController` subclasses:

protocol TrackedViewController: UIViewController {
    var analyticsTitle: String { get }
}

This is roughly equivalent to under your proposal:

protocol TrackedObject: class {
    var analyticsTitle: String { get }
}
typealias TrackedViewController = UIViewController & TrackedObject

But in this case we would be able to avoid the TrackedObject protocol
which we didn’t care about or want, but were required to create in the
process of creating our typealias.

Since we allow only one subclass, this gets a little tricky, right? If
TrackedViewController refines UIViewController, then should it count as a
subclass requirement when it comes to further composition and be required
first?

The syntax also implies that everything on UIViewController is a
requirement for a TrackedViewController. Would a class that conforms to
TrackedViewController automatically be a subclass of UIViewController, or
would only subclasses of UIViewController be allowed to conform to
TrackedViewController (i.e. is _being_ a UIViewController a requirement for
conforming to TrackedViewController)? The former seems surprising, at least
to me. Yet in the latter case, this would behave very differently from
protocols you put on the right side of the colon. Would it make sense to
allow a protocol to refine a struct, too?

IMO this idea might be best considered separately. Could be nice to have,
but it touches on a little more than existentials.

···

On Sun, Jan 29, 2017 at 4:19 PM, Matthew Johnson via swift-evolution < swift-evolution@swift.org> wrote:

On Jan 29, 2017, at 4:01 PM, David Hart <david@hartbit.com> wrote:
On 29 Jan 2017, at 18:30, Matthew Johnson <matthew@anandabits.com> wrote:

As only the first element in the existential composition syntax can be a
class type, and by extending this rule to typealias expansions, we can make
sure that we only need to read the first element to know if it contains a
class requirement.

I think this is unnecessarily limiting in a couple of ways. I agree that
a class should come first if it is mentioned explicitly***. I am less sure
we should require this when the type is part of a typealias combined with
other protocol requirements.

I agree with Chris that I think it’s important to require the class be
mentioned first when the class is mentioned explicitly. Even if we lost the
Any<Base, Protocol> syntax, there is still enough similarity to a class’s
inheritance/conformance clause to keep the consistency and readability.

For example, one use case I remember discussing with Austin is refining
supertype requirements. If I have a typealias which requires a superclass
`Base` I should be able to form an existential using that typealias that
*refines* that requirement to some type *Derived* which is a non-final
subtype of `Base`. This would require syntax that allows us to put a class
name in the first position, but also mention a typealias with a supertype
requirement in a subsequent position.

I’ve read the examples in the thread and I think I agree that those cases
should be accepted. But just to make sure we are on the same page, what
does everyone think of the validity of the following cases? For shorthand,
I use parentheses to represent typealias expansion. For example, when I
write:

Protocol1 & (Protocol2 & Protocol3)

I mean:

typealias Something = Protocol2 & Protocol3Protocol1 & Something

*Questions*

   1. Should class requirements be fixed to first position? I.e., should Protocol
   & Base be valid and equivalent to Base & Protocol?
   2. Should repetition of class requirements in the same declaration be
   allowed? I.e., should Base & Base be valid and equivalent to Base?
   3. Should repetition of class requirements through typealias expansion
   be allowed? I.e., should Base & (Base & Protocol) be valid and
   equivalent to Base & Protocol?
   4. Should type and sub-type requirements in the same declaration be
   allowed? I.e., should Base & Derived or Derived & Base be valid and
   equivalent to Derived?
   5. Should type and sub-type requirements through typealias expansion
   be allowed? I.e., should Base & (Derived & Protocol) or Derived &
   (Base & Protocol) be valid and equivalent to Derived & Protocol?

*My Answers*

   1. No, for the reasons stated above.
   2. No, because it doesn’t make sense to repeat it in the same
   declaration.
   3. Yes, I’m gonna start agreeing with you and think will ease
   typealias composition.
   4. No, for the same reasons as 2.
   5. Yes, for the same reasons as 3.

My answer depends on whether we adopt the perspective of intersection
types and allow things like `Base1 & Base2` or not. If we’re not going to
do that (at least not yet) I think your answers are the right ones.

However, if we decide to allow uninhabiatable existentials like`Base1 &
Base2` I think it would be best to just relax the rules such that they
follow the logic of intersection types.

The good news is that (as far as I can tell) it would be an additive
change to start with your rules and later adopt the intersection types
approach.

David.

Matthew

*** One argument against requiring a class to come first is that we could
conceptualize `&` as a type operator with a handful of overloads. This
would include both lhs and rhs are “protocol only kinds” as well as
overloads with a “protocol only kind” in either lhs or rhs and a “supertype
kind” in the other position. The tricky part of pulling this off would be
including an overload where both lhs and rhs have a “supertype kind”, but
only when the operands have a subtype / supertype relationship with each
other.

I suspect this conceptualization isn’t worth the complexity it brings, but
it is tempting to try and view `&` as a type operator. As long as this
only involves a loosening of restrictions it could probably be introduced
as an additive change down the road.

On Jan 29, 2017, at 10:39 AM, David Hart <david@hartbit.com> wrote:

Hello,

As promised, I wrote the first draft of a proposal to add class
requirements to the existential syntax. Please let me know what you think.

https://github.com/hartbit/swift-evolution/blob/subclass-
existentials/proposals/XXXX-subclass-existentials.md

Regards,
David.

Existentials for classes conforming to protocols

   - Proposal: SE-XXXX
   <https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md>
   - Authors: David Hart <http://github.com/hartbit/>, Austin Zheng
   <http://github.com/austinzheng>
   - Review Manager: TBD
   - Status: TBD

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#introduction>
Introduction

This proposal brings more expressive power to the type system by allowing
Swift to represent existentials of classes and subclasses which conform to
protocols.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#motivation>
Motivation

Currently, the only existentials which can be represented in Swift are
conformances to a set of protocols, using the &syntax:

let existential: Hashable & CustomStringConvertible

On the other hand, Objective-C is capable of expressing existentials of
subclasses conforming to protocols with the following syntax:

UIViewController<UITableViewDataSource, UITableViewDelegate>* existential;

We propose to provide similar expressive power to Swift, which will also
improve the bridging of those types from Objective-C.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#proposed-solution>Proposed
solution

The proposal keeps the existing & syntax but allows the first element,
and only the first, to be of class type. The equivalent declaration to the
above Objective-C declaration would look like this:

let existential: UIViewController & UITableViewDataSource & UITableViewDelegate

As in Objective-C, this existential represents classes which have
UIViewController in their parent inheritance hierarchy and which also
conform to the UITableViewDataSource and UITableViewDelegate protocols.

As only the first element in the existential composition syntax can be a
class type, and by extending this rule to typealias expansions, we can make
sure that we only need to read the first element to know if it contains a
class requirement. As a consequence, here is a list of valid and invalid
code and the reasons for them:

let a: Hashable & CustomStringConvertible// VALID: This is still valid, as beforelet b: MyObject & Hashable// VALID: This is the new rule which allows an object type in first positionlet c: CustomStringConvertible & MyObject// INVALID: MyObject is not allowed in second position. A fix-it should help transform it to:// let c: MyObject & CustomStringConvertibletypealias MyObjectStringConvertible = MyObject & CustomStringConvertiblelet d: Hashable & MyObjectStringConvertible// INVALID: The typealias expansion means that the type of d expands to Hashable & MyObject & CustomStringConvertible, which has the class in the wrong position. A fix-it should help transform it to:// let d: MyObjectStringConvertible & Hashabletypealias MyObjectStringConvertible = MyObject & CustomStringConvertiblelet e: MyOtherObject & MyObjectStringConvertible// INVALID: The typealias expansion would allow an existential with two class requirements, which is invalid

The following examples could technically be legal, but we believe we
should keep them invalid to keep the rules simple:

let a: MyObject & MyObject & CustomStringConvertible// This is equivalent to MyObject & CustomStringConvertiblelet b: MyObjectSubclass & MyObject & Hashable// This is equivalent to MyObjectSubclass & Hashabletypealias MyObjectStringConvertible = MyObject & CustomStringConvertiblelet d: MyObject & MyObjectStringConvertible// This is equivalent to MyObject & CustomStringConvertible

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#source-compatibility>Source
compatibility

This is a source breaking change. All types bridged from Objective-C which
use the equivalent Objective-C feature import without the protocol
conformances in Swift 3. This change would increase the existential's
requirement and break on code which does not meet the new protocol
requirements. For example, the following Objective-C code:

@interface MyViewController
- (void)setup:(nonnull UIViewController<UITableViewDataSource,UITableViewDelegate>*)tableViewController;@end

is imported into Swift 3 as:

class MyViewController {
    func setup(tableViewController: UIViewController) {}
}

which allows calling the function with an invalid parameter:

let myViewController: MyViewController()
myViewController.setup(UIViewController())

The previous code would have worked as long as the Objective-C code did
not call any method of UITableViewDataSource or UITableViewDelegate. But
if this proposal is accepted and implemented as-is, the Objective-C code
would now be imported as:

class MyViewController {
    func setup(tableViewController: UIViewController & UITableViewDataSource & UITableViewDelegate) {}
}

That would then cause the Swift code to fail to compile with an error
which states that UIViewController does not conform to the
UITableViewDataSource and UITableViewDelegate protocols.

It is a source-breaking change, but should have a minimal impact for the
following reasons:

   - Not many Objective-C code used the existential syntax in practice.
   - There generated errors are a good thing because they point out
   potential crashes which would have gone un-noticed.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#alternatives-considered>Alternatives
considered

None.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#acknowledgements>
Acknowledgements
Thanks to Austin Zheng <http://github.com/austinzheng> and Matthew Johnson
<https://github.com/anandabits> who brought a lot of attention to
existentials in this mailing-list and from whom most of the ideas in the
proposal come from.

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


(Matthew Johnson) #18

Hi Matthew,

I’ll reply to this post, because it allows me to discuss a few of the points in the discussion, but I’ve read the whole discussion.

Hi David,

This looks like a great start.

One thing we should consider is whether we tie this proposal so tightly to classes or whether it might be better to call these supertype constraints. The feature might also be useful for value types if / when Swift gets value subtyping.

This makes sense, especially with the Substring : String discussions going on. When I rework the proposal, I’ll try to make it more general.

One enhancement that might be worth considering. Specifically, allowing protocols to declare a specific supertype requirement in the place where a `class` constraint would usually be found. After this proposal we could already do something similar using a `class` constraint to define a protocol and a typealias to bind it to the supertype requirement. It seems like allowing users to state this more directly would be a good idea.

You lost me there. Can you give examples?

Sure. In an iOS app you might want to create a protocol that should only be conformed to by `UIViewController` subclasses:

protocol TrackedViewController: UIViewController {
    var analyticsTitle: String { get }
}

This is roughly equivalent to under your proposal:

protocol TrackedObject: class {
    var analyticsTitle: String { get }
}
typealias TrackedViewController = UIViewController & TrackedObject

But in this case we would be able to avoid the TrackedObject protocol which we didn’t care about or want, but were required to create in the process of creating our typealias.

Since we allow only one subclass, this gets a little tricky, right? If TrackedViewController refines UIViewController, then should it count as a subclass requirement when it comes to further composition and be required first?

It would be treated the same as the typealias as far as the syntax of composing existential types with `&` is concerned. I imagine the sensible thing, if we have the “class comes first requirement" is:

protocol P {}
typealias Foo = TrackedViewController & P // allowed
typealias Foo = P & TrackedViewController // not allowed

class MyViewController: UIVIewController, TrackedViewController {}

The syntax also implies that everything on UIViewController is a requirement for a TrackedViewController. Would a class that conforms to TrackedViewController automatically be a subclass of UIViewController, or would only subclasses of UIViewController be allowed to conform to TrackedViewController (i.e. is _being_ a UIViewController a requirement for conforming to TrackedViewController)? The former seems surprising, at least to me. Yet in the latter case, this would behave very differently from protocols you put on the right side of the colon.

I hadn’t considered this and it’s a good question. Conforming classes should still have to declare their immediate superclass. What it adds is a requirement that conforming types *eventually* have the constraining type as a supertype. A class can pick up implementation of other requirements from it’s superclass so why shouldn’t it’s superclass also be able to meet a supertype requirement.

Would it make sense to allow a protocol to refine a struct, too?

Eventually, yes. Immediately, maybe. I think this makes sense as soon as we get value subtyping and / or we adopt the intersection types view and allow uninhabited existentials. Until then we probably don’t gain anything from it.

Adding it immediately would reduce the need to make a future change and / or remember to add it when we add the other features and it probably wouldn’t cause any significant issues for users. On the other hand, it’s easy to make a case that adding it now is unnecessary and speculative complexity and we should wait until we have a real use for it.

IMO this idea might be best considered separately. Could be nice to have, but it touches on a little more than existentials.

Are we going to be allowed to use these existential types as generic constraints? That was allowed in Austin’s proposal. If we are going to support this it makes sense to me that we should have support for supertype requirements in protocols. After all, what is the role of a concrete type name in the existential syntax other than to introduce what is effectively an anonymous protocol with a supertype requirement?

···

On Jan 29, 2017, at 4:48 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Sun, Jan 29, 2017 at 4:19 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 29, 2017, at 4:01 PM, David Hart <david@hartbit.com <mailto:david@hartbit.com>> wrote:

On 29 Jan 2017, at 18:30, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

As only the first element in the existential composition syntax can be a class type, and by extending this rule to typealias expansions, we can make sure that we only need to read the first element to know if it contains a class requirement.

I think this is unnecessarily limiting in a couple of ways. I agree that a class should come first if it is mentioned explicitly***. I am less sure we should require this when the type is part of a typealias combined with other protocol requirements.

I agree with Chris that I think it’s important to require the class be mentioned first when the class is mentioned explicitly. Even if we lost the Any<Base, Protocol> syntax, there is still enough similarity to a class’s inheritance/conformance clause to keep the consistency and readability.

For example, one use case I remember discussing with Austin is refining supertype requirements. If I have a typealias which requires a superclass `Base` I should be able to form an existential using that typealias that *refines* that requirement to some type *Derived* which is a non-final subtype of `Base`. This would require syntax that allows us to put a class name in the first position, but also mention a typealias with a supertype requirement in a subsequent position.

I’ve read the examples in the thread and I think I agree that those cases should be accepted. But just to make sure we are on the same page, what does everyone think of the validity of the following cases? For shorthand, I use parentheses to represent typealias expansion. For example, when I write:

Protocol1 & (Protocol2 & Protocol3)
I mean:

typealias Something = Protocol2 & Protocol3
Protocol1 & Something
Questions

Should class requirements be fixed to first position? I.e., should Protocol & Base be valid and equivalent to Base & Protocol?
Should repetition of class requirements in the same declaration be allowed? I.e., should Base & Base be valid and equivalent to Base?
Should repetition of class requirements through typealias expansion be allowed? I.e., should Base & (Base & Protocol) be valid and equivalent to Base & Protocol?
Should type and sub-type requirements in the same declaration be allowed? I.e., should Base & Derived or Derived & Base be valid and equivalent to Derived?
Should type and sub-type requirements through typealias expansion be allowed? I.e., should Base & (Derived & Protocol) or Derived & (Base & Protocol) be valid and equivalent to Derived & Protocol?
My Answers

No, for the reasons stated above.
No, because it doesn’t make sense to repeat it in the same declaration.
Yes, I’m gonna start agreeing with you and think will ease typealias composition.
No, for the same reasons as 2.
Yes, for the same reasons as 3.

My answer depends on whether we adopt the perspective of intersection types and allow things like `Base1 & Base2` or not. If we’re not going to do that (at least not yet) I think your answers are the right ones.

However, if we decide to allow uninhabiatable existentials like`Base1 & Base2` I think it would be best to just relax the rules such that they follow the logic of intersection types.

The good news is that (as far as I can tell) it would be an additive change to start with your rules and later adopt the intersection types approach.

David.

Matthew

*** One argument against requiring a class to come first is that we could conceptualize `&` as a type operator with a handful of overloads. This would include both lhs and rhs are “protocol only kinds” as well as overloads with a “protocol only kind” in either lhs or rhs and a “supertype kind” in the other position. The tricky part of pulling this off would be including an overload where both lhs and rhs have a “supertype kind”, but only when the operands have a subtype / supertype relationship with each other.

I suspect this conceptualization isn’t worth the complexity it brings, but it is tempting to try and view `&` as a type operator. As long as this only involves a loosening of restrictions it could probably be introduced as an additive change down the road.

On Jan 29, 2017, at 10:39 AM, David Hart <david@hartbit.com <mailto:david@hartbit.com>> wrote:

Hello,

As promised, I wrote the first draft of a proposal to add class requirements to the existential syntax. Please let me know what you think.

https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md

Regards,
David.

Existentials for classes conforming to protocols

Proposal: SE-XXXX <https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md>
Authors: David Hart <http://github.com/hartbit/>, Austin Zheng <http://github.com/austinzheng>
Review Manager: TBD
Status: TBD
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#introduction>Introduction

This proposal brings more expressive power to the type system by allowing Swift to represent existentials of classes and subclasses which conform to protocols.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#motivation>Motivation

Currently, the only existentials which can be represented in Swift are conformances to a set of protocols, using the &syntax:

let existential: Hashable & CustomStringConvertible
On the other hand, Objective-C is capable of expressing existentials of subclasses conforming to protocols with the following syntax:

UIViewController<UITableViewDataSource, UITableViewDelegate>* existential;
We propose to provide similar expressive power to Swift, which will also improve the bridging of those types from Objective-C.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#proposed-solution>Proposed solution

The proposal keeps the existing & syntax but allows the first element, and only the first, to be of class type. The equivalent declaration to the above Objective-C declaration would look like this:

let existential: UIViewController & UITableViewDataSource & UITableViewDelegate
As in Objective-C, this existential represents classes which have UIViewController in their parent inheritance hierarchy and which also conform to the UITableViewDataSource and UITableViewDelegate protocols.

As only the first element in the existential composition syntax can be a class type, and by extending this rule to typealias expansions, we can make sure that we only need to read the first element to know if it contains a class requirement. As a consequence, here is a list of valid and invalid code and the reasons for them:

let a: Hashable & CustomStringConvertible
// VALID: This is still valid, as before

let b: MyObject & Hashable
// VALID: This is the new rule which allows an object type in first position

let c: CustomStringConvertible & MyObject
// INVALID: MyObject is not allowed in second position. A fix-it should help transform it to:
// let c: MyObject & CustomStringConvertible

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: Hashable & MyObjectStringConvertible
// INVALID: The typealias expansion means that the type of d expands to Hashable & MyObject & CustomStringConvertible, which has the class in the wrong position. A fix-it should help transform it to:
// let d: MyObjectStringConvertible & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let e: MyOtherObject & MyObjectStringConvertible
// INVALID: The typealias expansion would allow an existential with two class requirements, which is invalid
The following examples could technically be legal, but we believe we should keep them invalid to keep the rules simple:

let a: MyObject & MyObject & CustomStringConvertible
// This is equivalent to MyObject & CustomStringConvertible

let b: MyObjectSubclass & MyObject & Hashable
// This is equivalent to MyObjectSubclass & Hashable

typealias MyObjectStringConvertible = MyObject & CustomStringConvertible
let d: MyObject & MyObjectStringConvertible
// This is equivalent to MyObject & CustomStringConvertible
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#source-compatibility>Source compatibility

This is a source breaking change. All types bridged from Objective-C which use the equivalent Objective-C feature import without the protocol conformances in Swift 3. This change would increase the existential's requirement and break on code which does not meet the new protocol requirements. For example, the following Objective-C code:

@interface MyViewController
- (void)setup:(nonnull UIViewController<UITableViewDataSource,UITableViewDelegate>*)tableViewController;
@end
is imported into Swift 3 as:

class MyViewController {
    func setup(tableViewController: UIViewController) {}
}
which allows calling the function with an invalid parameter:

let myViewController: MyViewController()
myViewController.setup(UIViewController())
The previous code would have worked as long as the Objective-C code did not call any method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code would now be imported as:

class MyViewController {
    func setup(tableViewController: UIViewController & UITableViewDataSource & UITableViewDelegate) {}
}
That would then cause the Swift code to fail to compile with an error which states that UIViewController does not conform to the UITableViewDataSource and UITableViewDelegate protocols.

It is a source-breaking change, but should have a minimal impact for the following reasons:

Not many Objective-C code used the existential syntax in practice.
There generated errors are a good thing because they point out potential crashes which would have gone un-noticed.
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#alternatives-considered>Alternatives considered

None.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#acknowledgements>Acknowledgements

Thanks to Austin Zheng <http://github.com/austinzheng> and Matthew Johnson <https://github.com/anandabits> who brought a lot of attention to existentials in this mailing-list and from whom most of the ideas in the proposal come from.

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


(Xiaodi Wu) #19

Hi Matthew,

I’ll reply to this post, because it allows me to discuss a few of the
points in the discussion, but I’ve read the whole discussion.

Hi David,

This looks like a great start.

One thing we should consider is whether we tie this proposal so tightly
to classes or whether it might be better to call these supertype
constraints. The feature might also be useful for value types if / when
Swift gets value subtyping.

This makes sense, especially with the Substring : String discussions
going on. When I rework the proposal, I’ll try to make it more general.

One enhancement that might be worth considering. Specifically, allowing
protocols to declare a specific supertype requirement in the place where a
`class` constraint would usually be found. After this proposal we could
already do something similar using a `class` constraint to define a
protocol and a typealias to bind it to the supertype requirement. It seems
like allowing users to state this more directly would be a good idea.

You lost me there. Can you give examples?

Sure. In an iOS app you might want to create a protocol that should only
be conformed to by `UIViewController` subclasses:

protocol TrackedViewController: UIViewController {
    var analyticsTitle: String { get }
}

This is roughly equivalent to under your proposal:

protocol TrackedObject: class {
    var analyticsTitle: String { get }
}
typealias TrackedViewController = UIViewController & TrackedObject

But in this case we would be able to avoid the TrackedObject protocol
which we didn’t care about or want, but were required to create in the
process of creating our typealias.

Since we allow only one subclass, this gets a little tricky, right? If
TrackedViewController refines UIViewController, then should it count as a
subclass requirement when it comes to further composition and be required
first?

It would be treated the same as the typealias as far as the syntax of
composing existential types with `&` is concerned. I imagine the sensible
thing, if we have the “class comes first requirement" is:

protocol P {}
typealias Foo = TrackedViewController & P // allowed
typealias Foo = P & TrackedViewController // not allowed

class MyViewController: UIVIewController, TrackedViewController {}

The syntax also implies that everything on UIViewController is a
requirement for a TrackedViewController. Would a class that conforms to
TrackedViewController automatically be a subclass of UIViewController, or
would only subclasses of UIViewController be allowed to conform to
TrackedViewController (i.e. is _being_ a UIViewController a requirement for
conforming to TrackedViewController)? The former seems surprising, at least
to me. Yet in the latter case, this would behave very differently from
protocols you put on the right side of the colon.

I hadn’t considered this and it’s a good question. Conforming classes
should still have to declare their immediate superclass. What it adds is a
requirement that conforming types *eventually* have the constraining type
as a supertype. A class can pick up implementation of other requirements
from it’s superclass so why shouldn’t it’s superclass also be able to meet
a supertype requirement.

Would it make sense to allow a protocol to refine a struct, too?

Eventually, yes. Immediately, maybe. I think this makes sense as soon as
we get value subtyping and / or we adopt the intersection types view and
allow uninhabited existentials. Until then we probably don’t gain anything
from it.

Adding it immediately would reduce the need to make a future change and /
or remember to add it when we add the other features and it probably
wouldn’t cause any significant issues for users. On the other hand, it’s
easy to make a case that adding it now is unnecessary and speculative
complexity and we should wait until we have a real use for it.

IMO this idea might be best considered separately. Could be nice to have,
but it touches on a little more than existentials.

Are we going to be allowed to use these existential types as generic
constraints? That was allowed in Austin’s proposal. If we are going to
support this it makes sense to me that we should have support for supertype
requirements in protocols. After all, what is the role of a concrete type
name in the existential syntax other than to introduce what is effectively
an anonymous protocol with a supertype requirement?

I didn't think that supporting existential types as generic constraints was
part of _this_ proposal? It'd be nice to have that, but IMO it's a separate
discussion.

As only the first element in the existential composition syntax can be a

···

On Sun, Jan 29, 2017 at 6:00 PM, Matthew Johnson <matthew@anandabits.com> wrote:

On Jan 29, 2017, at 4:48 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Sun, Jan 29, 2017 at 4:19 PM, Matthew Johnson via swift-evolution < > swift-evolution@swift.org> wrote:

On Jan 29, 2017, at 4:01 PM, David Hart <david@hartbit.com> wrote:
On 29 Jan 2017, at 18:30, Matthew Johnson <matthew@anandabits.com> wrote:
class type, and by extending this rule to typealias expansions, we can make
sure that we only need to read the first element to know if it contains a
class requirement.

I think this is unnecessarily limiting in a couple of ways. I agree that
a class should come first if it is mentioned explicitly***. I am less sure
we should require this when the type is part of a typealias combined with
other protocol requirements.

I agree with Chris that I think it’s important to require the class be
mentioned first when the class is mentioned explicitly. Even if we lost the
Any<Base, Protocol> syntax, there is still enough similarity to a class’s
inheritance/conformance clause to keep the consistency and readability.

For example, one use case I remember discussing with Austin is refining
supertype requirements. If I have a typealias which requires a superclass
`Base` I should be able to form an existential using that typealias that
*refines* that requirement to some type *Derived* which is a non-final
subtype of `Base`. This would require syntax that allows us to put a class
name in the first position, but also mention a typealias with a supertype
requirement in a subsequent position.

I’ve read the examples in the thread and I think I agree that those cases
should be accepted. But just to make sure we are on the same page, what
does everyone think of the validity of the following cases? For shorthand,
I use parentheses to represent typealias expansion. For example, when I
write:

Protocol1 & (Protocol2 & Protocol3)

I mean:

typealias Something = Protocol2 & Protocol3Protocol1 & Something

*Questions*

   1. Should class requirements be fixed to first position? I.e., should Protocol
   & Base be valid and equivalent to Base & Protocol?
   2. Should repetition of class requirements in the same declaration be
   allowed? I.e., should Base & Base be valid and equivalent to Base?
   3. Should repetition of class requirements through typealias
   expansion be allowed? I.e., should Base & (Base & Protocol) be valid
   and equivalent to Base & Protocol?
   4. Should type and sub-type requirements in the same declaration be
   allowed? I.e., should Base & Derived or Derived & Base be valid and
   equivalent to Derived?
   5. Should type and sub-type requirements through typealias expansion
   be allowed? I.e., should Base & (Derived & Protocol) or Derived &
   (Base & Protocol) be valid and equivalent to Derived & Protocol?

*My Answers*

   1. No, for the reasons stated above.
   2. No, because it doesn’t make sense to repeat it in the same
   declaration.
   3. Yes, I’m gonna start agreeing with you and think will ease
   typealias composition.
   4. No, for the same reasons as 2.
   5. Yes, for the same reasons as 3.

My answer depends on whether we adopt the perspective of intersection
types and allow things like `Base1 & Base2` or not. If we’re not going to
do that (at least not yet) I think your answers are the right ones.

However, if we decide to allow uninhabiatable existentials like`Base1 &
Base2` I think it would be best to just relax the rules such that they
follow the logic of intersection types.

The good news is that (as far as I can tell) it would be an additive
change to start with your rules and later adopt the intersection types
approach.

David.

Matthew

*** One argument against requiring a class to come first is that we could
conceptualize `&` as a type operator with a handful of overloads. This
would include both lhs and rhs are “protocol only kinds” as well as
overloads with a “protocol only kind” in either lhs or rhs and a “supertype
kind” in the other position. The tricky part of pulling this off would be
including an overload where both lhs and rhs have a “supertype kind”, but
only when the operands have a subtype / supertype relationship with each
other.

I suspect this conceptualization isn’t worth the complexity it brings,
but it is tempting to try and view `&` as a type operator. As long as this
only involves a loosening of restrictions it could probably be introduced
as an additive change down the road.

On Jan 29, 2017, at 10:39 AM, David Hart <david@hartbit.com> wrote:

Hello,

As promised, I wrote the first draft of a proposal to add class
requirements to the existential syntax. Please let me know what you think.

https://github.com/hartbit/swift-evolution/blob/subclass-exi
stentials/proposals/XXXX-subclass-existentials.md

Regards,
David.

Existentials for classes conforming to protocols

   - Proposal: SE-XXXX
   <https://github.com/hartbit/swift-evolution/blob/subclass-existentials/proposals/XXXX-subclass-existentials.md>
   - Authors: David Hart <http://github.com/hartbit/>, Austin Zheng
   <http://github.com/austinzheng>
   - Review Manager: TBD
   - Status: TBD

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#introduction>
Introduction

This proposal brings more expressive power to the type system by allowing
Swift to represent existentials of classes and subclasses which conform to
protocols.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#motivation>
Motivation

Currently, the only existentials which can be represented in Swift are
conformances to a set of protocols, using the &syntax:

let existential: Hashable & CustomStringConvertible

On the other hand, Objective-C is capable of expressing existentials of
subclasses conforming to protocols with the following syntax:

UIViewController<UITableViewDataSource, UITableViewDelegate>* existential;

We propose to provide similar expressive power to Swift, which will also
improve the bridging of those types from Objective-C.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#proposed-solution>Proposed
solution

The proposal keeps the existing & syntax but allows the first element,
and only the first, to be of class type. The equivalent declaration to the
above Objective-C declaration would look like this:

let existential: UIViewController & UITableViewDataSource & UITableViewDelegate

As in Objective-C, this existential represents classes which have
UIViewController in their parent inheritance hierarchy and which also
conform to the UITableViewDataSource and UITableViewDelegate protocols.

As only the first element in the existential composition syntax can be a
class type, and by extending this rule to typealias expansions, we can make
sure that we only need to read the first element to know if it contains a
class requirement. As a consequence, here is a list of valid and invalid
code and the reasons for them:

let a: Hashable & CustomStringConvertible// VALID: This is still valid, as beforelet b: MyObject & Hashable// VALID: This is the new rule which allows an object type in first positionlet c: CustomStringConvertible & MyObject// INVALID: MyObject is not allowed in second position. A fix-it should help transform it to:// let c: MyObject & CustomStringConvertibletypealias MyObjectStringConvertible = MyObject & CustomStringConvertiblelet d: Hashable & MyObjectStringConvertible// INVALID: The typealias expansion means that the type of d expands to Hashable & MyObject & CustomStringConvertible, which has the class in the wrong position. A fix-it should help transform it to:// let d: MyObjectStringConvertible & Hashabletypealias MyObjectStringConvertible = MyObject & CustomStringConvertiblelet e: MyOtherObject & MyObjectStringConvertible// INVALID: The typealias expansion would allow an existential with two class requirements, which is invalid

The following examples could technically be legal, but we believe we
should keep them invalid to keep the rules simple:

let a: MyObject & MyObject & CustomStringConvertible// This is equivalent to MyObject & CustomStringConvertiblelet b: MyObjectSubclass & MyObject & Hashable// This is equivalent to MyObjectSubclass & Hashabletypealias MyObjectStringConvertible = MyObject & CustomStringConvertiblelet d: MyObject & MyObjectStringConvertible// This is equivalent to MyObject & CustomStringConvertible

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#source-compatibility>Source
compatibility

This is a source breaking change. All types bridged from Objective-C
which use the equivalent Objective-C feature import without the protocol
conformances in Swift 3. This change would increase the existential's
requirement and break on code which does not meet the new protocol
requirements. For example, the following Objective-C code:

@interface MyViewController
- (void)setup:(nonnull UIViewController<UITableViewDataSource,UITableViewDelegate>*)tableViewController;@end

is imported into Swift 3 as:

class MyViewController {
    func setup(tableViewController: UIViewController) {}
}

which allows calling the function with an invalid parameter:

let myViewController: MyViewController()
myViewController.setup(UIViewController())

The previous code would have worked as long as the Objective-C code did
not call any method of UITableViewDataSource or UITableViewDelegate. But
if this proposal is accepted and implemented as-is, the Objective-C code
would now be imported as:

class MyViewController {
    func setup(tableViewController: UIViewController & UITableViewDataSource & UITableViewDelegate) {}
}

That would then cause the Swift code to fail to compile with an error
which states that UIViewController does not conform to the
UITableViewDataSource and UITableViewDelegate protocols.

It is a source-breaking change, but should have a minimal impact for the
following reasons:

   - Not many Objective-C code used the existential syntax in practice.
   - There generated errors are a good thing because they point out
   potential crashes which would have gone un-noticed.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#alternatives-considered>Alternatives
considered

None.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials/proposals#acknowledgements>
Acknowledgements
Thanks to Austin Zheng <http://github.com/austinzheng> and Matthew
Johnson <https://github.com/anandabits> who brought a lot of attention
to existentials in this mailing-list and from whom most of the ideas in the
proposal come from.

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


(Xiaodi Wu) #20

Cool. Another avenue of improvement here is relaxing the single-class
spelling rule for the sake of composing typealiases.

As Matthew mentioned, if I have class Base and typealiases Foo = Base &
Protocol1 and Bar = Base & Protocol2, it'd be nice to allow Foo & Bar.

It'd be nice to go one step further: given class Derived : Base, if I have
typealiases Foo2 = Base & Protocol1 and Bar2 = Derived & Protocol2, then it
could be permitted to write Foo2 & Bar2, since there is effectively only
one subclass requirement (Derived).

As I understand it, the rationale for allowing only one subclass
requirement is that Swift supports only single inheritance. Thus, two
disparate subclass requirements Base1 & Base2 would make your existential
type essentially equivalent to Never. But Base1 & Base1 & Base1 is fine for
the type system, the implementation burden (though greater) shouldn't be
too awful, and you would measurably improve composition of typealiases.

···

On Sun, Jan 29, 2017 at 12:41 Austin Zheng <austinzheng@gmail.com> wrote:

The "class comes first" requirement made more sense when the proposed
syntax was still "Any<T, U, V>", intentionally mirroring how the superclass
and conformances are declared on a class declaration (the archives contain
more detailed arguments, both pro and con). Now that the syntax is "T & U &
V", I agree that privileging the class requirement is counterintuitive and
probably unhelpful.

Austin

> On Jan 29, 2017, at 10:37 AM, Matt Whiteside via swift-evolution < > swift-evolution@swift.org> wrote:
>
> Thanks for writing this proposal David.
>
>> On Jan 29, 2017, at 10:13, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:
>>
>> As Matthew mentioned, the rules can certainly later be relaxed, but
given that this proposal has the compiler generating fix-its for subclasses
in second position, is there a reason other than stylistic for demanding
MyClass & MyProtocol instead of MyProtocol & MyClass?
>>
>> From a naive perspective, it seems that if the compiler understands my
meaning perfectly, it should just accept that spelling rather than complain.
>
> I had that thought too. Since ‘and’ is a symmetric operation, requiring
the class to be in the first position seems counter-intuitive.
>
> -Matt
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution