Class and Subclass Existentials (Round 2)


(David Hart) #1

Hello mailing-list,

I rewrote the draft proposal concerning the class and subclass existentials. Please let me know what you think, especially concerning the class and AnyObject conundrum.

Regards,
David.

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

Class and Subtype existentials

Proposal: SE-XXXX <https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/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/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#introduction>Introduction

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

<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#motivation>Motivation

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

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

id<Protocol1, Protocol2>
Base<Protocol>*
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/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#proposed-solution>Proposed solution

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

class & Protocol1 & Protocol2
Base & Protocol
As in Objective-C, the first line is an existential of classes which conform to Protocol1 and Protocol2, and the second line is an existential of subtypes of Base which conform to Protocol.

Here are the new proposed rules for what is valid in a existential conjunction syntax:

<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#1-the-first-element-in-the-protocol-composition-syntax-can-be-the-class-keyword-to-enforce-a-class-constraint>1. The first element in the protocol composition syntax can be the class keyword to enforce a class constraint:

protocol P {}
struct S : P {}
class C : P {}
let t: P & class // Compiler error: class requirement must be in first position
let u: class & P = S() // Compiler error: S is not of class type
let v: class & P = C() // Compiles successfully
<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#2-the-first-element-in-the-protocol-composition-syntax-can-be-a-class-type-to-enforce-the-existential-to-be-a-subtype-of-the-class>2. The first element in the protocol composition syntax can be a class type to enforce the existential to be a subtype of the class:

protocol P {}
struct S {}
class C {}
class D : P {}
class E : C, P {}
let t: P & C // Compiler error: subclass contraint must be in first position
let u: S & P // Compiler error: S is not of class type
let v: C & P = D() // Compiler error: D is not a subtype of C
let w: C & P = E() // Compiles successfully
<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#3-when-a-protocol-composition-type-contains-a-typealias-the-validity-of-the-type-is-determined-using-the-following-steps>3. When a protocol composition type contains a typealias, the validity of the type is determined using the following steps:

Expand the typealias
Normalize the type by removing duplicate constraints and replacing less specific constraints by more specific constraints (a class constraint is less specific than a class type constraint, which is less specific than a constraint of a subclass of that class).
Check that the type does not contain two class-type constraints
class C {}
class D : C {}
class E {}
protocol P1 {}
protocol P2 {}
typealias TA1 = class & P1
typealias TA2 = class & P2
typealias TA3 = C & P2
typealias TA4 = D & P2
typealias TA5 = E & P2

typealias TA5 = TA1 & TA2
typealias TA5 = class & P1 & class & P2 // Expansion
typealias TA5 = class & P1 & P2 // Normalization
// TA5 is valid

typealias TA6 = TA1 & TA3
typealias TA6 = class & P1 & C & P2 // Expansion
typealias TA6 = C & P1 & P2 // Normalization (class < C)
// TA6 is valid

typealias TA7 = TA3 & TA4
typealias TA7 = C & P2 & D & P2 // Expansion
typealias TA7 = D & P2 // Normalization (C < D)
// TA7 is valid

typealias TA8 = TA4 & TA5
typealias TA8 = D & P2 & E & P2 // Expansion
typealias TA8 = D & E & P2 // Normalization
// TA8 is invalid because the D and E constraints are incompatible
<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#class-and-anyobject>class and AnyObject

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. They are four solutions to this dilemna:

Do nothing.
Replace all uses of AnyObject by class, breaking source compatibility.
Replace all uses of class by AnyObject, breaking source compatibility.
Redefine AnyObject as typealias AnyObject = class.
<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#source-compatibility>Source compatibility

Leaving aside what is decided concerning class and AnyObject, this change will not break Swift 3 compability mode because Objective-C types will continue to be imported as before. But in Swift 4 mode, all types bridged from Objective-C which use the equivalent Objective-C existential syntax could break 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 mode 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 continues to compile but still crashs if the Objective-C code calls a method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code will be imported in Swift 4 mode as:

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

<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#alternatives-considered>Alternatives considered

None.

<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#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) #2

Hello mailing-list,

I rewrote the draft proposal concerning the class and subclass existentials. Please let me know what you think, especially concerning the class and AnyObject conundrum.

Thanks for working on this! Some comments below.

  - Doug

Regards,
David.

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

Class and Subtype existentials

Proposal: SE-XXXX <https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/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/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#introduction>Introduction

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

<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#motivation>Motivation

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

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

id<Protocol1, Protocol2>
Base<Protocol>*
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/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#proposed-solution>Proposed solution

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

class & Protocol1 & Protocol2
Base & Protocol
As in Objective-C, the first line is an existential of classes which conform to Protocol1 and Protocol2, and the second line is an existential of subtypes of Base which conform to Protocol.

Here are the new proposed rules for what is valid in a existential conjunction syntax:

<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#1-the-first-element-in-the-protocol-composition-syntax-can-be-the-class-keyword-to-enforce-a-class-constraint>1. The first element in the protocol composition syntax can be the class keyword to enforce a class constraint:

protocol P {}
struct S : P {}
class C : P {}
let t: P & class // Compiler error: class requirement must be in first position
let u: class & P = S() // Compiler error: S is not of class type
let v: class & P = C() // Compiles successfully

Is

  let w: class = C()

intended to work? (I believe the answer is “yes”)

2. The first element in the protocol composition syntax can be a class type to enforce the existential to be a subtype of the class:

protocol P {}
struct S {}
class C {}
class D : P {}
class E : C, P {}
let t: P & C // Compiler error: subclass contraint must be in first position
let u: S & P // Compiler error: S is not of class type
let v: C & P = D() // Compiler error: D is not a subtype of C
let w: C & P = E() // Compiles successfully

Okay.

3. When a protocol composition type contains a typealias, the validity of the type is determined using the following steps:

Expand the typealias
Normalize the type by removing duplicate constraints and replacing less specific constraints by more specific constraints (a class constraint is less specific than a class type constraint, which is less specific than a constraint of a subclass of that class).
Check that the type does not contain two class-type constraints

Technically, if one class is a subclass of the other, we can take the subclass. We actually already do this in generic constraints:

  class C { }
  class D: C { }

  func f<T>(_: T) where T: C, T: D { } // same as just "where T: D”

We could be more strict with existentials, but it seems unnecessary.

<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#class-and-anyobject>class and AnyObject

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. They are four solutions to this dilemna:

Do nothing.
Replace all uses of AnyObject by class, breaking source compatibility.
Replace all uses of class by AnyObject, breaking source compatibility.
Redefine AnyObject as typealias AnyObject = class.

Please pick one to propose; it improves clarify for the proposal to be specific. The others can go to “Alternatives considered”.

I suggest #4 :slight_smile:

Also, I think it’s good to point out that

  extension AnyObject { }

is already banned, so making AnyObject not a protocol shouldn’t actually change what code is accepted.

Source compatibility

Leaving aside what is decided concerning class and AnyObject, this change will not break Swift 3 compability mode because Objective-C types will continue to be imported as before. But in Swift 4 mode, all types bridged from Objective-C which use the equivalent Objective-C existential syntax could break code which does not meet the new protocol requirements.

Can you make the “import Objective-C’s SomeClass<SomeProto> as SomeClass & SomeProto” bit its own section before “Source compatibility”? The “Source compatibility” bit is more informational and shouldn’t introduce additional parts of the proposal.

For example, the following Objective-C code:

@interface MyViewController
- (void)setup:(nonnull UIViewController<UITableViewDataSource,UITableViewDelegate>*)tableViewController;
@end
is imported into Swift-3 mode 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 continues to compile but still crashs if the Objective-C code calls a method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code will be imported in Swift 4 mode as:

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

<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#alternatives-considered>

  - Doug

···

On Feb 6, 2017, at 1:18 PM, David Hart <david@hartbit.com> wrote:


(Matthew Johnson) #3

This looks really good, thank you for your work on this proposal David!

I agree with Doug’s suggestion to pick #4 (typealias AnyObject = class) and move the other options to alternatives considered. This seems like the best way to go given Swift’s source compatibility promise and will probably be the least controversial option as well.

···

On Feb 6, 2017, at 3:18 PM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

Hello mailing-list,

I rewrote the draft proposal concerning the class and subclass existentials. Please let me know what you think, especially concerning the class and AnyObject conundrum.

Regards,
David.

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

Class and Subtype existentials

Proposal: SE-XXXX <https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/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/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#introduction>Introduction

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

<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#motivation>Motivation

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

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

id<Protocol1, Protocol2>
Base<Protocol>*
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/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#proposed-solution>Proposed solution

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

class & Protocol1 & Protocol2
Base & Protocol
As in Objective-C, the first line is an existential of classes which conform to Protocol1 and Protocol2, and the second line is an existential of subtypes of Base which conform to Protocol.

Here are the new proposed rules for what is valid in a existential conjunction syntax:

<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#1-the-first-element-in-the-protocol-composition-syntax-can-be-the-class-keyword-to-enforce-a-class-constraint>1. The first element in the protocol composition syntax can be the class keyword to enforce a class constraint:

protocol P {}
struct S : P {}
class C : P {}
let t: P & class // Compiler error: class requirement must be in first position
let u: class & P = S() // Compiler error: S is not of class type
let v: class & P = C() // Compiles successfully
<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#2-the-first-element-in-the-protocol-composition-syntax-can-be-a-class-type-to-enforce-the-existential-to-be-a-subtype-of-the-class>2. The first element in the protocol composition syntax can be a class type to enforce the existential to be a subtype of the class:

protocol P {}
struct S {}
class C {}
class D : P {}
class E : C, P {}
let t: P & C // Compiler error: subclass contraint must be in first position
let u: S & P // Compiler error: S is not of class type
let v: C & P = D() // Compiler error: D is not a subtype of C
let w: C & P = E() // Compiles successfully
<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#3-when-a-protocol-composition-type-contains-a-typealias-the-validity-of-the-type-is-determined-using-the-following-steps>3. When a protocol composition type contains a typealias, the validity of the type is determined using the following steps:

Expand the typealias
Normalize the type by removing duplicate constraints and replacing less specific constraints by more specific constraints (a class constraint is less specific than a class type constraint, which is less specific than a constraint of a subclass of that class).
Check that the type does not contain two class-type constraints
class C {}
class D : C {}
class E {}
protocol P1 {}
protocol P2 {}
typealias TA1 = class & P1
typealias TA2 = class & P2
typealias TA3 = C & P2
typealias TA4 = D & P2
typealias TA5 = E & P2

typealias TA5 = TA1 & TA2
typealias TA5 = class & P1 & class & P2 // Expansion
typealias TA5 = class & P1 & P2 // Normalization
// TA5 is valid

typealias TA6 = TA1 & TA3
typealias TA6 = class & P1 & C & P2 // Expansion
typealias TA6 = C & P1 & P2 // Normalization (class < C)
// TA6 is valid

typealias TA7 = TA3 & TA4
typealias TA7 = C & P2 & D & P2 // Expansion
typealias TA7 = D & P2 // Normalization (C < D)
// TA7 is valid

typealias TA8 = TA4 & TA5
typealias TA8 = D & P2 & E & P2 // Expansion
typealias TA8 = D & E & P2 // Normalization
// TA8 is invalid because the D and E constraints are incompatible
<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#class-and-anyobject>class and AnyObject

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. They are four solutions to this dilemna:

Do nothing.
Replace all uses of AnyObject by class, breaking source compatibility.
Replace all uses of class by AnyObject, breaking source compatibility.
Redefine AnyObject as typealias AnyObject = class.
<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#source-compatibility>Source compatibility

Leaving aside what is decided concerning class and AnyObject, this change will not break Swift 3 compability mode because Objective-C types will continue to be imported as before. But in Swift 4 mode, all types bridged from Objective-C which use the equivalent Objective-C existential syntax could break 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 mode 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 continues to compile but still crashs if the Objective-C code calls a method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code will be imported in Swift 4 mode as:

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

<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#alternatives-considered>Alternatives considered

None.

<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#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


(Adrian Zubarev) #4

Looks really promising to me. :slight_smile:

Aside the class existentials, I still miss generalized struct and enum constraints (this is out of scope of this proposal, and also not meant to be a request). Sometimes I really want to tell someone who’s using my code base “no you’re not allowed to create a class type here, because the way you’re doing it is ugly, as if Objective-C, Java and C# had a child called quasimodo”. By default such constraints would not enforce value semantics, but at least they’ll do some good to some codebase.

Speaking of existentials again, struct & ProtocolFoo & ProtocolBar would be the same as if we had protocol FooBar : struct, ProtocolFoo, ProtocolBar {}. Such an existential/protocol will reject any class instance.

Alternatively there could exist AnyValue which would cover any value type including tuples. (As a sidenote: IMHO AnyValue should not conform to something like ValueSemantics by default, because there might be some point where you don’t want value semantics to apply.)

···

--
Adrian Zubarev
Sent with Airmail

Am 6. Februar 2017 um 22:18:19, David Hart via swift-evolution (swift-evolution@swift.org) schrieb:

Hello mailing-list,

I rewrote the draft proposal concerning the class and subclass existentials. Please let me know what you think, especially concerning the class and AnyObject conundrum.

Regards,
David.

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

Class and Subtype existentials
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 subtypes which conform to protocols.

Motivation

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

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

id<Protocol1, Protocol2>
Base<Protocol>*
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 either the class keyword or of class type. The equivalent to the above Objective-C types would look like this:

class & Protocol1 & Protocol2
Base & Protocol
As in Objective-C, the first line is an existential of classes which conform to Protocol1 and Protocol2, and the second line is an existential of subtypes of Base which conform to Protocol.

Here are the new proposed rules for what is valid in a existential conjunction syntax:

1. The first element in the protocol composition syntax can be the class keyword to enforce a class constraint:

protocol P {}
struct S : P {}
class C : P {}
let t: P & class // Compiler error: class requirement must be in first position
let u: class & P = S() // Compiler error: S is not of class type
let v: class & P = C() // Compiles successfully
2. The first element in the protocol composition syntax can be a class type to enforce the existential to be a subtype of the class:

protocol P {}
struct S {}
class C {}
class D : P {}
class E : C, P {}
let t: P & C // Compiler error: subclass contraint must be in first position
let u: S & P // Compiler error: S is not of class type
let v: C & P = D() // Compiler error: D is not a subtype of C
let w: C & P = E() // Compiles successfully
3. When a protocol composition type contains a typealias, the validity of the type is determined using the following steps:

Expand the typealias
Normalize the type by removing duplicate constraints and replacing less specific constraints by more specific constraints (a class constraint is less specific than a class type constraint, which is less specific than a constraint of a subclass of that class).
Check that the type does not contain two class-type constraints
class C {}
class D : C {}
class E {}
protocol P1 {}
protocol P2 {}
typealias TA1 = class & P1
typealias TA2 = class & P2
typealias TA3 = C & P2
typealias TA4 = D & P2
typealias TA5 = E & P2

typealias TA5 = TA1 & TA2
typealias TA5 = class & P1 & class & P2 // Expansion
typealias TA5 = class & P1 & P2 // Normalization
// TA5 is valid

typealias TA6 = TA1 & TA3
typealias TA6 = class & P1 & C & P2 // Expansion
typealias TA6 = C & P1 & P2 // Normalization (class < C)
// TA6 is valid

typealias TA7 = TA3 & TA4
typealias TA7 = C & P2 & D & P2 // Expansion
typealias TA7 = D & P2 // Normalization (C < D)
// TA7 is valid

typealias TA8 = TA4 & TA5
typealias TA8 = D & P2 & E & P2 // Expansion
typealias TA8 = D & E & P2 // Normalization
// TA8 is invalid because the D and E constraints are incompatible
class and AnyObject

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. They are four solutions to this dilemna:

Do nothing.
Replace all uses of AnyObject by class, breaking source compatibility.
Replace all uses of class by AnyObject, breaking source compatibility.
Redefine AnyObject as typealias AnyObject = class.
Source compatibility

Leaving aside what is decided concerning class and AnyObject, this change will not break Swift 3 compability mode because Objective-C types will continue to be imported as before. But in Swift 4 mode, all types bridged from Objective-C which use the equivalent Objective-C existential syntax could break 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 mode 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 continues to compile but still crashs if the Objective-C code calls a method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code will be imported in Swift 4 mode as:

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

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


(Step C) #5

Looks good. Minor comments below:

let t: P & C // Compiler error: subclass contraint must be in first position

Typo: contraint should be constraint

...
typealias TA4 = D & P2
typealias TA5 = E & P2

typealias TA5 = TA1 & TA2
typealias TA5 = class & P1 & class & P2 // Expansion
typealias TA5 = class & P1 & P2 // Normalization
// TA5 is valid

The typealias 'T5' is repeated as both an initial composition, and as a demonstration of combining typealiases.

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. They are four solutions to this dilemna:
Do nothing.
Replace all uses of AnyObject by class, breaking source compatibility.
Replace all uses of class by AnyObject, breaking source compatibility.
Redefine AnyObject as typealias AnyObject = class.

I agree with other comments on recommending 4 here, and covering the others as alternatives.


(Joe Groff) #6

-Joe

···

On Feb 9, 2017, at 4:26 AM, Step Christopher via swift-evolution <swift-evolution@swift.org> wrote:

Looks good. Minor comments below:

let t: P & C // Compiler error: subclass contraint must be in first position

Typo: contraint should be constraint

<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#3-when-a-protocol-composition-type-contains-a-typealias-the-validity-of-the-type-is-determined-using-the-following-steps>
...
typealias TA4 = D & P2
typealias TA5 = E & P2

typealias TA5 = TA1 & TA2
typealias TA5 = class & P1 & class & P2 // Expansion
typealias TA5 = class & P1 & P2 // Normalization
// TA5 is valid

The typealias 'T5' is repeated as both an initial composition, and as a demonstration of combining typealiases.

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. They are four solutions to this dilemna:
Do nothing.
Replace all uses of AnyObject by class, breaking source compatibility.
Replace all uses of class by AnyObject, breaking source compatibility.
Redefine AnyObject as typealias AnyObject = class.

I agree with other comments on recommending 4 here, and covering the others as alternatives

<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#source-compatibility>I agree that we need the typealias for compatibility. I think it's still worth discussing whether the `AnyObject` typealias should *only* be there for compatibility; it could be deprecated or obsoleted in Swift 4 or future language versions.


(Hooman Mehr) #7

Looks good. Minor comments below:
The typealias 'T5' is repeated as both an initial composition, and as a demonstration of combining typealiases.

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. They are four solutions to this dilemna:
Do nothing.
Replace all uses of AnyObject by class, breaking source compatibility.
Replace all uses of class by AnyObject, breaking source compatibility.
Redefine AnyObject as typealias AnyObject = class.

I agree with other comments on recommending 4 here, and covering the others as alternatives

<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#source-compatibility>I agree that we need the typealias for compatibility. I think it's still worth discussing whether the `AnyObject` typealias should *only* be there for compatibility; it could be deprecated or obsoleted in Swift 4 or future language versions.

I think it might be worth keeping to provide a more sensible capitalization alternative than lower case “class” when used as a type name:

var obj: class // this looks weird because of capitalization.

var obj: AnyObject // this looks better.

···

On Feb 9, 2017, at 10:47 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 9, 2017, at 4:26 AM, Step Christopher via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

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


(Matthew Johnson) #8

Looks good. Minor comments below:
The typealias 'T5' is repeated as both an initial composition, and as a demonstration of combining typealiases.

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. They are four solutions to this dilemna:
Do nothing.
Replace all uses of AnyObject by class, breaking source compatibility.
Replace all uses of class by AnyObject, breaking source compatibility.
Redefine AnyObject as typealias AnyObject = class.

I agree with other comments on recommending 4 here, and covering the others as alternatives

I agree that we need the typealias for compatibility. I think it's still worth discussing whether the `AnyObject` typealias should *only* be there for compatibility; it could be deprecated or obsoleted in Swift 4 or future language versions.

I think it might be worth keeping to provide a more sensible capitalization alternative than lower case “class” when used as a type name:

var obj: class // this looks weird because of capitalization.

var obj: AnyObject // this looks better.

I agree that it looks better and would choose AnyObject if source compatibility weren't an issue. One option that wasn't listed was to drop 'class' but use a multi-release deprecation strategy and a fix-it to facilitate a smooth transition. If the community is willing to adopt this approach it would be my first choice.

···

Sent from my iPad

On Feb 9, 2017, at 1:30 PM, Hooman Mehr via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 9, 2017, at 10:47 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:
On Feb 9, 2017, at 4:26 AM, Step Christopher via swift-evolution <swift-evolution@swift.org> wrote:

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


(David Hart) #9

Sent from my iPad

Looks good. Minor comments below:
The typealias 'T5' is repeated as both an initial composition, and as a demonstration of combining typealiases.

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. They are four solutions to this dilemna:
Do nothing.
Replace all uses of AnyObject by class, breaking source compatibility.
Replace all uses of class by AnyObject, breaking source compatibility.
Redefine AnyObject as typealias AnyObject = class.

I agree with other comments on recommending 4 here, and covering the others as alternatives

I agree that we need the typealias for compatibility. I think it's still worth discussing whether the `AnyObject` typealias should *only* be there for compatibility; it could be deprecated or obsoleted in Swift 4 or future language versions.

I think it might be worth keeping to provide a more sensible capitalization alternative than lower case “class” when used as a type name:

var obj: class // this looks weird because of capitalization.

var obj: AnyObject // this looks better.

I agree that it looks better and would choose AnyObject if source compatibility weren't an issue. One option that wasn't listed was to drop 'class' but use a multi-release deprecation strategy and a fix-it to facilitate a smooth transition. If the community is willing to adopt this approach it would be my first choice.

You mean option 3?

···

On 9 Feb 2017, at 20:43, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 9, 2017, at 1:30 PM, Hooman Mehr via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 9, 2017, at 10:47 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:
On Feb 9, 2017, at 4:26 AM, Step Christopher via swift-evolution <swift-evolution@swift.org> wrote:

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

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


(Matthew Johnson) #10

Sent from my iPad

Looks good. Minor comments below:
The typealias 'T5' is repeated as both an initial composition, and as a demonstration of combining typealiases.

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. They are four solutions to this dilemna:
Do nothing.
Replace all uses of AnyObject by class, breaking source compatibility.
Replace all uses of class by AnyObject, breaking source compatibility.
Redefine AnyObject as typealias AnyObject = class.

I agree with other comments on recommending 4 here, and covering the others as alternatives

<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#source-compatibility>I agree that we need the typealias for compatibility. I think it's still worth discussing whether the `AnyObject` typealias should *only* be there for compatibility; it could be deprecated or obsoleted in Swift 4 or future language versions.

I think it might be worth keeping to provide a more sensible capitalization alternative than lower case “class” when used as a type name:

var obj: class // this looks weird because of capitalization.

var obj: AnyObject // this looks better.

I agree that it looks better and would choose AnyObject if source compatibility weren't an issue. One option that wasn't listed was to drop 'class' but use a multi-release deprecation strategy and a fix-it to facilitate a smooth transition. If the community is willing to adopt this approach it would be my first choice.

You mean option 3?

Pretty much, but option 3 does not make it clear that it won’t break source immediately in Swift 4. I think it becomes much more reasonable if Swift 3.1 code still compiles in Swift 4 mode, but with a deprecation warning.

The reason I prefer `AnyObject` to `class` is because I think it’s ugly to have `class` as the name of an existential type. Type names are uppercase in Swift. It is also used to compose with protocols which also use uppercase names in Swift. Because it appears in contexts which use an uppercase convention it makes sense for this to have an uppercase name. `AnyObject` seems like the obvious choice if we’re going to go in that direction.

···

On Feb 9, 2017, at 2:44 PM, David Hart <david@hartbit.com> wrote:
On 9 Feb 2017, at 20:43, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 9, 2017, at 1:30 PM, Hooman Mehr via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 9, 2017, at 10:47 AM, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 9, 2017, at 4:26 AM, Step Christopher via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

-Joe
_______________________________________________
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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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


(David Hart) #11

Hi Matthew,

Your arguments made sense to me. I modified the proposal to choose strategy number 3: deprecating and removing class over several versions to favour AnyObject. Mind having another proof read?

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

Anybody has counter arguments?

Class and Subtype existentials

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#introduction>Introduction

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

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

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

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

id<Protocol1, Protocol2>
Base<Protocol>*
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#proposed-solution>Proposed solution

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

AnyObject & Protocol1 & Protocol2
Base & Protocol
As in Objective-C, the first line is an existential of classes which conform to Protocol1 and Protocol2, and the second line is an existential of subtypes of Base which conform to Protocol.

Here are the new proposed rules for what is valid in a existential conjunction syntax:

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#1-the-first-element-in-the-protocol-composition-syntax-can-be-the-anyobject-keyword-to-enforce-a-class-constraint>1. The first element in the protocol composition syntax can be the AnyObject keyword to enforce a class constraint:

protocol P {}
struct S : P {}
class C : P {}
let t: P & AnyObject // Compiler error: AnyObject requirement must be in first position
let u: AnyObject & P = S() // Compiler error: S is not of class type
let v: AnyObject & P = C() // Compiles successfully
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#2-the-first-element-in-the-protocol-composition-syntax-can-be-a-class-type-to-enforce-the-existential-to-be-a-subtype-of-the-class>2. The first element in the protocol composition syntax can be a class type to enforce the existential to be a subtype of the class:

protocol P {}
struct S {}
class C {}
class D : P {}
class E : C, P {}
let t: P & C // Compiler error: subclass constraint must be in first position
let u: S & P // Compiler error: S is not of class type
let v: C & P = D() // Compiler error: D is not a subtype of C
let w: C & P = E() // Compiles successfully
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#3-when-a-protocol-composition-type-contains-a-typealias-the-validity-of-the-type-is-determined-using-the-following-steps>3. When a protocol composition type contains a typealias, the validity of the type is determined using the following steps:

Expand the typealias
Normalize the type by removing duplicate constraints and replacing less specific constraints by more specific constraints (a class constraint is less specific than a class type constraint, which is less specific than a constraint of a subclass of that class).
Check that the type does not contain two class-type constraints
class C {}
class D : C {}
class E {}
protocol P1 {}
protocol P2 {}
typealias TA1 = AnyObject & P1
typealias TA2 = AnyObject & P2
typealias TA3 = C & P2
typealias TA4 = D & P2
typealias TA5 = E & P2

typealias TA5 = TA1 & TA2
// Expansion: typealias TA5 = AnyObject & P1 & AnyObject & P2
// Normalization: typealias TA5 = AnyObject & P1 & P2
// TA5 is valid

typealias TA6 = TA1 & TA3
// Expansion: typealias TA6 = AnyObject & P1 & C & P2
// Normalization (AnyObject < C): typealias TA6 = C & P1 & P2
// TA6 is valid

typealias TA7 = TA3 & TA4
// Expansion: typealias TA7 = C & P2 & D & P2
// Normalization (C < D): typealias TA7 = D & P2
// TA7 is valid

typealias TA8 = TA4 & TA5
// Expansion: typealias TA8 = D & P2 & E & P2
// Normalization: typealias TA8 = D & E & P2
// TA8 is invalid because the D and E constraints are incompatible
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#class-and-anyobject>class and AnyObject

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. To get rid of the duplication, we suggest only keeping AnyObject around. To reduce source-breakage to a minimum, class could be redefined as typealias class = AnyObject and give a deprecation warning on class for the first version of Swift this proposal is implemented in. Later, class could be removed in a subsequent version of Swift.

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

This change will not break Swift 3 compability mode because Objective-C types will continue to be imported as before. But in Swift 4 mode, all types bridged from Objective-C which use the equivalent Objective-C existential syntax could break 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 mode 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 continues to compile but still crashs if the Objective-C code calls a method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code will be imported in Swift 4 mode as:

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

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

An alternative solution to the class/AnyObject duplication was to keep both, redefine AnyObject as typealias AnyObject = class and favor the latter when used as a type name.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#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.

···

On 9 Feb 2017, at 21:50, Matthew Johnson <matthew@anandabits.com> wrote:

On Feb 9, 2017, at 2:44 PM, David Hart <david@hartbit.com <mailto:david@hartbit.com>> wrote:

On 9 Feb 2017, at 20:43, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Sent from my iPad

On Feb 9, 2017, at 1:30 PM, Hooman Mehr via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 9, 2017, at 10:47 AM, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 9, 2017, at 4:26 AM, Step Christopher via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Looks good. Minor comments below:
The typealias 'T5' is repeated as both an initial composition, and as a demonstration of combining typealiases.

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. They are four solutions to this dilemna:
Do nothing.
Replace all uses of AnyObject by class, breaking source compatibility.
Replace all uses of class by AnyObject, breaking source compatibility.
Redefine AnyObject as typealias AnyObject = class.

I agree with other comments on recommending 4 here, and covering the others as alternatives

<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#source-compatibility>I agree that we need the typealias for compatibility. I think it's still worth discussing whether the `AnyObject` typealias should *only* be there for compatibility; it could be deprecated or obsoleted in Swift 4 or future language versions.

I think it might be worth keeping to provide a more sensible capitalization alternative than lower case “class” when used as a type name:

var obj: class // this looks weird because of capitalization.

var obj: AnyObject // this looks better.

I agree that it looks better and would choose AnyObject if source compatibility weren't an issue. One option that wasn't listed was to drop 'class' but use a multi-release deprecation strategy and a fix-it to facilitate a smooth transition. If the community is willing to adopt this approach it would be my first choice.

You mean option 3?

Pretty much, but option 3 does not make it clear that it won’t break source immediately in Swift 4. I think it becomes much more reasonable if Swift 3.1 code still compiles in Swift 4 mode, but with a deprecation warning.

The reason I prefer `AnyObject` to `class` is because I think it’s ugly to have `class` as the name of an existential type. Type names are uppercase in Swift. It is also used to compose with protocols which also use uppercase names in Swift. Because it appears in contexts which use an uppercase convention it makes sense for this to have an uppercase name. `AnyObject` seems like the obvious choice if we’re going to go in that direction.

-Joe
_______________________________________________
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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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


(Alejandro Martinez) #12

On class vs. AnyObject, probablt a dumb question but maybe worth
clarifying, we don't pretend to change it in a class declaration
right?

like

AnyObject A {
var ....
}

apart from that looking forward for this!

···

On Sun, Feb 12, 2017 at 8:32 PM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

Hi Matthew,

Your arguments made sense to me. I modified the proposal to choose strategy
number 3: deprecating and removing class over several versions to favour
AnyObject. Mind having another proof read?

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

Anybody has counter arguments?

Class and Subtype existentials

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 subtypes which conform to
protocols.

Motivation

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

Protocol1 & Protocol2

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

id<Protocol1, Protocol2>
Base<Protocol>*

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 either the AnyObjectkeyword or of class type. The
equivalent to the above Objective-C types would look like this:

AnyObject & Protocol1 & Protocol2
Base & Protocol

As in Objective-C, the first line is an existential of classes which conform
to Protocol1 and Protocol2, and the second line is an existential of
subtypes of Base which conform to Protocol.

Here are the new proposed rules for what is valid in a existential
conjunction syntax:

1. The first element in the protocol composition syntax can be the AnyObject
keyword to enforce a class constraint:

protocol P {}
struct S : P {}
class C : P {}
let t: P & AnyObject // Compiler error: AnyObject requirement must be in
first position
let u: AnyObject & P = S() // Compiler error: S is not of class type
let v: AnyObject & P = C() // Compiles successfully

2. The first element in the protocol composition syntax can be a class type
to enforce the existential to be a subtype of the class:

protocol P {}
struct S {}
class C {}
class D : P {}
class E : C, P {}
let t: P & C // Compiler error: subclass constraint must be in first
position
let u: S & P // Compiler error: S is not of class type
let v: C & P = D() // Compiler error: D is not a subtype of C
let w: C & P = E() // Compiles successfully

3. When a protocol composition type contains a typealias, the validity of
the type is determined using the following steps:

Expand the typealias
Normalize the type by removing duplicate constraints and replacing less
specific constraints by more specific constraints (a class constraint is
less specific than a class type constraint, which is less specific than a
constraint of a subclass of that class).
Check that the type does not contain two class-type constraints

class C {}
class D : C {}
class E {}
protocol P1 {}
protocol P2 {}
typealias TA1 = AnyObject & P1
typealias TA2 = AnyObject & P2
typealias TA3 = C & P2
typealias TA4 = D & P2
typealias TA5 = E & P2

typealias TA5 = TA1 & TA2
// Expansion: typealias TA5 = AnyObject & P1 & AnyObject & P2
// Normalization: typealias TA5 = AnyObject & P1 & P2
// TA5 is valid

typealias TA6 = TA1 & TA3
// Expansion: typealias TA6 = AnyObject & P1 & C & P2
// Normalization (AnyObject < C): typealias TA6 = C & P1 & P2
// TA6 is valid

typealias TA7 = TA3 & TA4
// Expansion: typealias TA7 = C & P2 & D & P2
// Normalization (C < D): typealias TA7 = D & P2
// TA7 is valid

typealias TA8 = TA4 & TA5
// Expansion: typealias TA8 = D & P2 & E & P2
// Normalization: typealias TA8 = D & E & P2
// TA8 is invalid because the D and E constraints are incompatible

class and AnyObject

This proposal merges the concepts of class and AnyObject, which now have the
same meaning: they represent an existential for classes. To get rid of the
duplication, we suggest only keeping AnyObject around. To reduce
source-breakage to a minimum, class could be redefined as typealias class =
AnyObject and give a deprecation warning on class for the first version of
Swift this proposal is implemented in. Later, class could be removed in a
subsequent version of Swift.

Source compatibility

This change will not break Swift 3 compability mode because Objective-C
types will continue to be imported as before. But in Swift 4 mode, all types
bridged from Objective-C which use the equivalent Objective-C existential
syntax could break 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 mode 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 continues to compile but still crashs if the Objective-C
code calls a method of UITableViewDataSource or UITableViewDelegate. But if
this proposal is accepted and implemented as-is, the Objective-C code will
be imported in Swift 4 mode as:

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

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

Alternatives considered

An alternative solution to the class/AnyObject duplication was to keep both,
redefine AnyObject as typealias AnyObject = class and favor the latter when
used as a type name.

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.

On 9 Feb 2017, at 21:50, Matthew Johnson <matthew@anandabits.com> wrote:

On Feb 9, 2017, at 2:44 PM, David Hart <david@hartbit.com> wrote:

On 9 Feb 2017, at 20:43, Matthew Johnson via swift-evolution > <swift-evolution@swift.org> wrote:

Sent from my iPad

On Feb 9, 2017, at 1:30 PM, Hooman Mehr via swift-evolution > <swift-evolution@swift.org> wrote:

On Feb 9, 2017, at 10:47 AM, Joe Groff via swift-evolution > <swift-evolution@swift.org> wrote:

On Feb 9, 2017, at 4:26 AM, Step Christopher via swift-evolution > <swift-evolution@swift.org> wrote:
Looks good. Minor comments below:
The typealias 'T5' is repeated as both an initial composition, and as a
demonstration of combining typealiases.

This proposal merges the concepts of class and AnyObject, which now have the
same meaning: they represent an existential for classes. They are four
solutions to this dilemna:

Do nothing.
Replace all uses of AnyObject by class, breaking source compatibility.
Replace all uses of class by AnyObject, breaking source compatibility.
Redefine AnyObject as typealias AnyObject = class.

I agree with other comments on recommending 4 here, and covering the others
as alternatives

I agree that we need the typealias for compatibility. I think it's still
worth discussing whether the `AnyObject` typealias should *only* be there
for compatibility; it could be deprecated or obsoleted in Swift 4 or future
language versions.

I think it might be worth keeping to provide a more sensible capitalization
alternative than lower case “class” when used as a type name:

var obj: class // this looks weird because of capitalization.

var obj: AnyObject // this looks better.

I agree that it looks better and would choose AnyObject if source
compatibility weren't an issue. One option that wasn't listed was to drop
'class' but use a multi-release deprecation strategy and a fix-it to
facilitate a smooth transition. If the community is willing to adopt this
approach it would be my first choice.

You mean option 3?

Pretty much, but option 3 does not make it clear that it won’t break source
immediately in Swift 4. I think it becomes much more reasonable if Swift
3.1 code still compiles in Swift 4 mode, but with a deprecation warning.

The reason I prefer `AnyObject` to `class` is because I think it’s ugly to
have `class` as the name of an existential type. Type names are uppercase
in Swift. It is also used to compose with protocols which also use
uppercase names in Swift. Because it appears in contexts which use an
uppercase convention it makes sense for this to have an uppercase name.
`AnyObject` seems like the obvious choice if we’re going to go in that
direction.

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

_______________________________________________
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

--
Alejandro Martinez
http://alejandromp.com


(Slava Pestov) #13

Hi Matthew,

Your arguments made sense to me. I modified the proposal to choose strategy number 3: deprecating and removing class over several versions to favour AnyObject. Mind having another proof read?

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

Anybody has counter arguments?

Class and Subtype existentials

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#introduction>Introduction

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

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

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

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

id<Protocol1, Protocol2>
Base<Protocol>*
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#proposed-solution>Proposed solution

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

AnyObject & Protocol1 & Protocol2
Base & Protocol
As in Objective-C, the first line is an existential of classes which conform to Protocol1 and Protocol2, and the second line is an existential of subtypes of Base which conform to Protocol.

Here are the new proposed rules for what is valid in a existential conjunction syntax:

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#1-the-first-element-in-the-protocol-composition-syntax-can-be-the-anyobject-keyword-to-enforce-a-class-constraint>1. The first element in the protocol composition syntax can be the AnyObject keyword to enforce a class constraint:

protocol P {}
struct S : P {}
class C : P {}
let t: P & AnyObject // Compiler error: AnyObject requirement must be in first position
let u: AnyObject & P = S() // Compiler error: S is not of class type
let v: AnyObject & P = C() // Compiles successfully
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#2-the-first-element-in-the-protocol-composition-syntax-can-be-a-class-type-to-enforce-the-existential-to-be-a-subtype-of-the-class>2. The first element in the protocol composition syntax can be a class type to enforce the existential to be a subtype of the class:

protocol P {}
struct S {}
class C {}
class D : P {}
class E : C, P {}
let t: P & C // Compiler error: subclass constraint must be in first position
let u: S & P // Compiler error: S is not of class type
let v: C & P = D() // Compiler error: D is not a subtype of C
let w: C & P = E() // Compiles successfully
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#3-when-a-protocol-composition-type-contains-a-typealias-the-validity-of-the-type-is-determined-using-the-following-steps>3. When a protocol composition type contains a typealias, the validity of the type is determined using the following steps:

Expand the typealias
Normalize the type by removing duplicate constraints and replacing less specific constraints by more specific constraints (a class constraint is less specific than a class type constraint, which is less specific than a constraint of a subclass of that class).
Check that the type does not contain two class-type constraints

You could generalize this and instead say that if the type contains two class-type constraints, the resulting existential type is the common base class of the two classes, or AnyObject if they do not share a common base class.

Also, I’d like to see some discussion about class-constrained existentials appearing in the inheritance clause of a protocol. IMHO, we should ban this:

typealias MyType = SomeClass & SomeProtocol

protocol SomeOtherProtocol : MyType {}

Slava

···

On Feb 12, 2017, at 12:32 PM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

class C {}
class D : C {}
class E {}
protocol P1 {}
protocol P2 {}
typealias TA1 = AnyObject & P1
typealias TA2 = AnyObject & P2
typealias TA3 = C & P2
typealias TA4 = D & P2
typealias TA5 = E & P2

typealias TA5 = TA1 & TA2
// Expansion: typealias TA5 = AnyObject & P1 & AnyObject & P2
// Normalization: typealias TA5 = AnyObject & P1 & P2
// TA5 is valid

typealias TA6 = TA1 & TA3
// Expansion: typealias TA6 = AnyObject & P1 & C & P2
// Normalization (AnyObject < C): typealias TA6 = C & P1 & P2
// TA6 is valid

typealias TA7 = TA3 & TA4
// Expansion: typealias TA7 = C & P2 & D & P2
// Normalization (C < D): typealias TA7 = D & P2
// TA7 is valid

typealias TA8 = TA4 & TA5
// Expansion: typealias TA8 = D & P2 & E & P2
// Normalization: typealias TA8 = D & E & P2
// TA8 is invalid because the D and E constraints are incompatible
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#class-and-anyobject>class and AnyObject

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. To get rid of the duplication, we suggest only keeping AnyObject around. To reduce source-breakage to a minimum, class could be redefined as typealias class = AnyObject and give a deprecation warning on class for the first version of Swift this proposal is implemented in. Later, class could be removed in a subsequent version of Swift.

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

This change will not break Swift 3 compability mode because Objective-C types will continue to be imported as before. But in Swift 4 mode, all types bridged from Objective-C which use the equivalent Objective-C existential syntax could break 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 mode 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 continues to compile but still crashs if the Objective-C code calls a method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code will be imported in Swift 4 mode as:

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

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

An alternative solution to the class/AnyObject duplication was to keep both, redefine AnyObject as typealias AnyObject = class and favor the latter when used as a type name.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#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.

On 9 Feb 2017, at 21:50, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

On Feb 9, 2017, at 2:44 PM, David Hart <david@hartbit.com <mailto:david@hartbit.com>> wrote:

On 9 Feb 2017, at 20:43, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Sent from my iPad

On Feb 9, 2017, at 1:30 PM, Hooman Mehr via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 9, 2017, at 10:47 AM, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 9, 2017, at 4:26 AM, Step Christopher via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Looks good. Minor comments below:
The typealias 'T5' is repeated as both an initial composition, and as a demonstration of combining typealiases.

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. They are four solutions to this dilemna:
Do nothing.
Replace all uses of AnyObject by class, breaking source compatibility.
Replace all uses of class by AnyObject, breaking source compatibility.
Redefine AnyObject as typealias AnyObject = class.

I agree with other comments on recommending 4 here, and covering the others as alternatives

<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#source-compatibility>I agree that we need the typealias for compatibility. I think it's still worth discussing whether the `AnyObject` typealias should *only* be there for compatibility; it could be deprecated or obsoleted in Swift 4 or future language versions.

I think it might be worth keeping to provide a more sensible capitalization alternative than lower case “class” when used as a type name:

var obj: class // this looks weird because of capitalization.

var obj: AnyObject // this looks better.

I agree that it looks better and would choose AnyObject if source compatibility weren't an issue. One option that wasn't listed was to drop 'class' but use a multi-release deprecation strategy and a fix-it to facilitate a smooth transition. If the community is willing to adopt this approach it would be my first choice.

You mean option 3?

Pretty much, but option 3 does not make it clear that it won’t break source immediately in Swift 4. I think it becomes much more reasonable if Swift 3.1 code still compiles in Swift 4 mode, but with a deprecation warning.

The reason I prefer `AnyObject` to `class` is because I think it’s ugly to have `class` as the name of an existential type. Type names are uppercase in Swift. It is also used to compose with protocols which also use uppercase names in Swift. Because it appears in contexts which use an uppercase convention it makes sense for this to have an uppercase name. `AnyObject` seems like the obvious choice if we’re going to go in that direction.

-Joe
_______________________________________________
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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
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


(Adrian Zubarev) #14

No, you’re totally right there. The mentioned class keyword is the protocol constraint-keyword such as protocol X : >> class << { … }. Instead we would like to generalize AnyObject

···

--
Adrian Zubarev
Sent with Airmail

Am 13. Februar 2017 um 17:34:18, Alejandro Martinez via swift-evolution (swift-evolution@swift.org) schrieb:

On class vs. AnyObject, probablt a dumb question but maybe worth
clarifying, we don't pretend to change it in a class declaration
right?

like

AnyObject A {
var ....
}

apart from that looking forward for this!

On Sun, Feb 12, 2017 at 8:32 PM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

Hi Matthew,

Your arguments made sense to me. I modified the proposal to choose strategy
number 3: deprecating and removing class over several versions to favour
AnyObject. Mind having another proof read?

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

Anybody has counter arguments?

Class and Subtype existentials

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 subtypes which conform to
protocols.

Motivation

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

Protocol1 & Protocol2

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

id<Protocol1, Protocol2>
Base<Protocol>*

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 either the AnyObjectkeyword or of class type. The
equivalent to the above Objective-C types would look like this:

AnyObject & Protocol1 & Protocol2
Base & Protocol

As in Objective-C, the first line is an existential of classes which conform
to Protocol1 and Protocol2, and the second line is an existential of
subtypes of Base which conform to Protocol.

Here are the new proposed rules for what is valid in a existential
conjunction syntax:

1. The first element in the protocol composition syntax can be the AnyObject
keyword to enforce a class constraint:

protocol P {}
struct S : P {}
class C : P {}
let t: P & AnyObject // Compiler error: AnyObject requirement must be in
first position
let u: AnyObject & P = S() // Compiler error: S is not of class type
let v: AnyObject & P = C() // Compiles successfully

2. The first element in the protocol composition syntax can be a class type
to enforce the existential to be a subtype of the class:

protocol P {}
struct S {}
class C {}
class D : P {}
class E : C, P {}
let t: P & C // Compiler error: subclass constraint must be in first
position
let u: S & P // Compiler error: S is not of class type
let v: C & P = D() // Compiler error: D is not a subtype of C
let w: C & P = E() // Compiles successfully

3. When a protocol composition type contains a typealias, the validity of
the type is determined using the following steps:

Expand the typealias
Normalize the type by removing duplicate constraints and replacing less
specific constraints by more specific constraints (a class constraint is
less specific than a class type constraint, which is less specific than a
constraint of a subclass of that class).
Check that the type does not contain two class-type constraints

class C {}
class D : C {}
class E {}
protocol P1 {}
protocol P2 {}
typealias TA1 = AnyObject & P1
typealias TA2 = AnyObject & P2
typealias TA3 = C & P2
typealias TA4 = D & P2
typealias TA5 = E & P2

typealias TA5 = TA1 & TA2
// Expansion: typealias TA5 = AnyObject & P1 & AnyObject & P2
// Normalization: typealias TA5 = AnyObject & P1 & P2
// TA5 is valid

typealias TA6 = TA1 & TA3
// Expansion: typealias TA6 = AnyObject & P1 & C & P2
// Normalization (AnyObject < C): typealias TA6 = C & P1 & P2
// TA6 is valid

typealias TA7 = TA3 & TA4
// Expansion: typealias TA7 = C & P2 & D & P2
// Normalization (C < D): typealias TA7 = D & P2
// TA7 is valid

typealias TA8 = TA4 & TA5
// Expansion: typealias TA8 = D & P2 & E & P2
// Normalization: typealias TA8 = D & E & P2
// TA8 is invalid because the D and E constraints are incompatible

class and AnyObject

This proposal merges the concepts of class and AnyObject, which now have the
same meaning: they represent an existential for classes. To get rid of the
duplication, we suggest only keeping AnyObject around. To reduce
source-breakage to a minimum, class could be redefined as typealias class =
AnyObject and give a deprecation warning on class for the first version of
Swift this proposal is implemented in. Later, class could be removed in a
subsequent version of Swift.

Source compatibility

This change will not break Swift 3 compability mode because Objective-C
types will continue to be imported as before. But in Swift 4 mode, all types
bridged from Objective-C which use the equivalent Objective-C existential
syntax could break 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 mode 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 continues to compile but still crashs if the Objective-C
code calls a method of UITableViewDataSource or UITableViewDelegate. But if
this proposal is accepted and implemented as-is, the Objective-C code will
be imported in Swift 4 mode as:

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

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

Alternatives considered

An alternative solution to the class/AnyObject duplication was to keep both,
redefine AnyObject as typealias AnyObject = class and favor the latter when
used as a type name.

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.

On 9 Feb 2017, at 21:50, Matthew Johnson <matthew@anandabits.com> wrote:

On Feb 9, 2017, at 2:44 PM, David Hart <david@hartbit.com> wrote:

On 9 Feb 2017, at 20:43, Matthew Johnson via swift-evolution > <swift-evolution@swift.org> wrote:

Sent from my iPad

On Feb 9, 2017, at 1:30 PM, Hooman Mehr via swift-evolution > <swift-evolution@swift.org> wrote:

On Feb 9, 2017, at 10:47 AM, Joe Groff via swift-evolution > <swift-evolution@swift.org> wrote:

On Feb 9, 2017, at 4:26 AM, Step Christopher via swift-evolution > <swift-evolution@swift.org> wrote:
Looks good. Minor comments below:
The typealias 'T5' is repeated as both an initial composition, and as a
demonstration of combining typealiases.

This proposal merges the concepts of class and AnyObject, which now have the
same meaning: they represent an existential for classes. They are four
solutions to this dilemna:

Do nothing.
Replace all uses of AnyObject by class, breaking source compatibility.
Replace all uses of class by AnyObject, breaking source compatibility.
Redefine AnyObject as typealias AnyObject = class.

I agree with other comments on recommending 4 here, and covering the others
as alternatives

I agree that we need the typealias for compatibility. I think it's still
worth discussing whether the `AnyObject` typealias should *only* be there
for compatibility; it could be deprecated or obsoleted in Swift 4 or future
language versions.

I think it might be worth keeping to provide a more sensible capitalization
alternative than lower case “class” when used as a type name:

var obj: class // this looks weird because of capitalization.

var obj: AnyObject // this looks better.

I agree that it looks better and would choose AnyObject if source
compatibility weren't an issue. One option that wasn't listed was to drop
'class' but use a multi-release deprecation strategy and a fix-it to
facilitate a smooth transition. If the community is willing to adopt this
approach it would be my first choice.

You mean option 3?

Pretty much, but option 3 does not make it clear that it won’t break source
immediately in Swift 4. I think it becomes much more reasonable if Swift
3.1 code still compiles in Swift 4 mode, but with a deprecation warning.

The reason I prefer `AnyObject` to `class` is because I think it’s ugly to
have `class` as the name of an existential type. Type names are uppercase
in Swift. It is also used to compose with protocols which also use
uppercase names in Swift. Because it appears in contexts which use an
uppercase convention it makes sense for this to have an uppercase name.
`AnyObject` seems like the obvious choice if we’re going to go in that
direction.

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

_______________________________________________
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

--
Alejandro Martinez
http://alejandromp.com
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Adrian Zubarev) #15

Why can’t we completely ban this?

protocol A {}
protocol B {}
typealias AB = A & B

protocol C : AB {} // Allowed, but could be also banned

protocol D : A & B {} // Error

···

--
Adrian Zubarev
Sent with Airmail

Am 14. Februar 2017 um 10:25:43, Slava Pestov via swift-evolution (swift-evolution@swift.org) schrieb:

On Feb 12, 2017, at 12:32 PM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

Hi Matthew,

Your arguments made sense to me. I modified the proposal to choose strategy number 3: deprecating and removing class over several versions to favour AnyObject. Mind having another proof read?

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

Anybody has counter arguments?

Class and Subtype existentials
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 subtypes which conform to protocols.

Motivation

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

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

id<Protocol1, Protocol2>
Base<Protocol>*
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 either the AnyObjectkeyword or of class type. The equivalent to the above Objective-C types would look like this:

AnyObject & Protocol1 & Protocol2
Base & Protocol
As in Objective-C, the first line is an existential of classes which conform to Protocol1 and Protocol2, and the second line is an existential of subtypes of Base which conform to Protocol.

Here are the new proposed rules for what is valid in a existential conjunction syntax:

1. The first element in the protocol composition syntax can be the AnyObject keyword to enforce a class constraint:

protocol P {}
struct S : P {}
class C : P {}
let t: P & AnyObject // Compiler error: AnyObject requirement must be in first position
let u: AnyObject & P = S() // Compiler error: S is not of class type
let v: AnyObject & P = C() // Compiles successfully
2. The first element in the protocol composition syntax can be a class type to enforce the existential to be a subtype of the class:

protocol P {}
struct S {}
class C {}
class D : P {}
class E : C, P {}
let t: P & C // Compiler error: subclass constraint must be in first position
let u: S & P // Compiler error: S is not of class type
let v: C & P = D() // Compiler error: D is not a subtype of C
let w: C & P = E() // Compiles successfully
3. When a protocol composition type contains a typealias, the validity of the type is determined using the following steps:

Expand the typealias
Normalize the type by removing duplicate constraints and replacing less specific constraints by more specific constraints (a class constraint is less specific than a class type constraint, which is less specific than a constraint of a subclass of that class).
Check that the type does not contain two class-type constraints

You could generalize this and instead say that if the type contains two class-type constraints, the resulting existential type is the common base class of the two classes, or AnyObject if they do not share a common base class.

Also, I’d like to see some discussion about class-constrained existentials appearing in the inheritance clause of a protocol. IMHO, we should ban this:

typealias MyType = SomeClass & SomeProtocol

protocol SomeOtherProtocol : MyType {}

Slava

class C {}
class D : C {}
class E {}
protocol P1 {}
protocol P2 {}
typealias TA1 = AnyObject & P1
typealias TA2 = AnyObject & P2
typealias TA3 = C & P2
typealias TA4 = D & P2
typealias TA5 = E & P2

typealias TA5 = TA1 & TA2
// Expansion: typealias TA5 = AnyObject & P1 & AnyObject & P2
// Normalization: typealias TA5 = AnyObject & P1 & P2
// TA5 is valid

typealias TA6 = TA1 & TA3
// Expansion: typealias TA6 = AnyObject & P1 & C & P2
// Normalization (AnyObject < C): typealias TA6 = C & P1 & P2
// TA6 is valid

typealias TA7 = TA3 & TA4
// Expansion: typealias TA7 = C & P2 & D & P2
// Normalization (C < D): typealias TA7 = D & P2
// TA7 is valid

typealias TA8 = TA4 & TA5
// Expansion: typealias TA8 = D & P2 & E & P2
// Normalization: typealias TA8 = D & E & P2
// TA8 is invalid because the D and E constraints are incompatible
class and AnyObject

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. To get rid of the duplication, we suggest only keeping AnyObject around. To reduce source-breakage to a minimum, class could be redefined as typealias class = AnyObject and give a deprecation warning on class for the first version of Swift this proposal is implemented in. Later, class could be removed in a subsequent version of Swift.

Source compatibility

This change will not break Swift 3 compability mode because Objective-C types will continue to be imported as before. But in Swift 4 mode, all types bridged from Objective-C which use the equivalent Objective-C existential syntax could break 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 mode 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 continues to compile but still crashs if the Objective-C code calls a method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code will be imported in Swift 4 mode as:

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

Alternatives considered

An alternative solution to the class/AnyObject duplication was to keep both, redefine AnyObject as typealias AnyObject = class and favor the latter when used as a type name.

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.

On 9 Feb 2017, at 21:50, Matthew Johnson <matthew@anandabits.com> wrote:

On Feb 9, 2017, at 2:44 PM, David Hart <david@hartbit.com> wrote:

On 9 Feb 2017, at 20:43, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

Sent from my iPad

On Feb 9, 2017, at 1:30 PM, Hooman Mehr via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 9, 2017, at 10:47 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:
On Feb 9, 2017, at 4:26 AM, Step Christopher via swift-evolution <swift-evolution@swift.org> wrote:
Looks good. Minor comments below:
The typealias 'T5' is repeated as both an initial composition, and as a demonstration of combining typealiases.

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. They are four solutions to this dilemna:
Do nothing.
Replace all uses of AnyObject by class, breaking source compatibility.
Replace all uses of class by AnyObject, breaking source compatibility.
Redefine AnyObject as typealias AnyObject = class.
I agree with other comments on recommending 4 here, and covering the others as alternatives

I agree that we need the typealias for compatibility. I think it's still worth discussing whether the `AnyObject` typealias should *only* be there for compatibility; it could be deprecated or obsoleted in Swift 4 or future language versions.

I think it might be worth keeping to provide a more sensible capitalization alternative than lower case “class” when used as a type name:

var obj: class // this looks weird because of capitalization.

var obj: AnyObject // this looks better.

I agree that it looks better and would choose AnyObject if source compatibility weren't an issue. One option that wasn't listed was to drop 'class' but use a multi-release deprecation strategy and a fix-it to facilitate a smooth transition. If the community is willing to adopt this approach it would be my first choice.

You mean option 3?

Pretty much, but option 3 does not make it clear that it won’t break source immediately in Swift 4. I think it becomes much more reasonable if Swift 3.1 code still compiles in Swift 4 mode, but with a deprecation warning.

The reason I prefer `AnyObject` to `class` is because I think it’s ugly to have `class` as the name of an existential type. Type names are uppercase in Swift. It is also used to compose with protocols which also use uppercase names in Swift. Because it appears in contexts which use an uppercase convention it makes sense for this to have an uppercase name. `AnyObject` seems like the obvious choice if we’re going to go in that direction.

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

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


(Hooman Mehr) #16

Why would we do that? They are called constraints, and we use “&” between them. They should move towards the more specific subclass, not the less restrained base class. So, I would say:

If the type contains two class-type constraints, it is only acceptable if one class constrain is a subclass of the other and the resulting constrain will be the subclass.

···

On Feb 14, 2017, at 1:24 AM, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:

3. When a protocol composition type contains a typealias, the validity of the type is determined using the following steps:

Expand the typealias
Normalize the type by removing duplicate constraints and replacing less specific constraints by more specific constraints (a class constraint is less specific than a class type constraint, which is less specific than a constraint of a subclass of that class).
Check that the type does not contain two class-type constraints

You could generalize this and instead say that if the type contains two class-type constraints, the resulting existential type is the common base class of the two classes, or AnyObject if they do not share a common base class.


(David Hart) #17

Hi Matthew,

Your arguments made sense to me. I modified the proposal to choose strategy number 3: deprecating and removing class over several versions to favour AnyObject. Mind having another proof read?

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

Anybody has counter arguments?

Class and Subtype existentials
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 subtypes which conform to protocols.

Motivation

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

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

id<Protocol1, Protocol2>
Base<Protocol>*
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 either the AnyObjectkeyword or of class type. The equivalent to the above Objective-C types would look like this:

AnyObject & Protocol1 & Protocol2
Base & Protocol
As in Objective-C, the first line is an existential of classes which conform to Protocol1 and Protocol2, and the second line is an existential of subtypes of Base which conform to Protocol.

Here are the new proposed rules for what is valid in a existential conjunction syntax:

1. The first element in the protocol composition syntax can be the AnyObject keyword to enforce a class constraint:

protocol P {}
struct S : P {}
class C : P {}
let t: P & AnyObject // Compiler error: AnyObject requirement must be in first position
let u: AnyObject & P = S() // Compiler error: S is not of class type
let v: AnyObject & P = C() // Compiles successfully
2. The first element in the protocol composition syntax can be a class type to enforce the existential to be a subtype of the class:

protocol P {}
struct S {}
class C {}
class D : P {}
class E : C, P {}
let t: P & C // Compiler error: subclass constraint must be in first position
let u: S & P // Compiler error: S is not of class type
let v: C & P = D() // Compiler error: D is not a subtype of C
let w: C & P = E() // Compiles successfully
3. When a protocol composition type contains a typealias, the validity of the type is determined using the following steps:

Expand the typealias
Normalize the type by removing duplicate constraints and replacing less specific constraints by more specific constraints (a class constraint is less specific than a class type constraint, which is less specific than a constraint of a subclass of that class).
Check that the type does not contain two class-type constraints

You could generalize this and instead say that if the type contains two class-type constraints, the resulting existential type is the common base class of the two classes, or AnyObject if they do not share a common base class.

But if they share a common base class, the existential is invalid. Did I misunderstand your generalization?

Also, I’d like to see some discussion about class-constrained existentials appearing in the inheritance clause of a protocol. IMHO, we should ban this:

typealias MyType = SomeClass & SomeProtocol

protocol SomeOtherProtocol : MyType {}

Yep, I'll make that clear. It should be disallowed IMHO. Thanks!

···

On 14 Feb 2017, at 10:24, Slava Pestov <spestov@apple.com> wrote:

On Feb 12, 2017, at 12:32 PM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

Slava

class C {}
class D : C {}
class E {}
protocol P1 {}
protocol P2 {}
typealias TA1 = AnyObject & P1
typealias TA2 = AnyObject & P2
typealias TA3 = C & P2
typealias TA4 = D & P2
typealias TA5 = E & P2

typealias TA5 = TA1 & TA2
// Expansion: typealias TA5 = AnyObject & P1 & AnyObject & P2
// Normalization: typealias TA5 = AnyObject & P1 & P2
// TA5 is valid

typealias TA6 = TA1 & TA3
// Expansion: typealias TA6 = AnyObject & P1 & C & P2
// Normalization (AnyObject < C): typealias TA6 = C & P1 & P2
// TA6 is valid

typealias TA7 = TA3 & TA4
// Expansion: typealias TA7 = C & P2 & D & P2
// Normalization (C < D): typealias TA7 = D & P2
// TA7 is valid

typealias TA8 = TA4 & TA5
// Expansion: typealias TA8 = D & P2 & E & P2
// Normalization: typealias TA8 = D & E & P2
// TA8 is invalid because the D and E constraints are incompatible
class and AnyObject

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. To get rid of the duplication, we suggest only keeping AnyObject around. To reduce source-breakage to a minimum, class could be redefined as typealias class = AnyObject and give a deprecation warning on class for the first version of Swift this proposal is implemented in. Later, class could be removed in a subsequent version of Swift.

Source compatibility

This change will not break Swift 3 compability mode because Objective-C types will continue to be imported as before. But in Swift 4 mode, all types bridged from Objective-C which use the equivalent Objective-C existential syntax could break 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 mode 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 continues to compile but still crashs if the Objective-C code calls a method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code will be imported in Swift 4 mode as:

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

Alternatives considered

An alternative solution to the class/AnyObject duplication was to keep both, redefine AnyObject as typealias AnyObject = class and favor the latter when used as a type name.

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.

On 9 Feb 2017, at 21:50, Matthew Johnson <matthew@anandabits.com> wrote:

On Feb 9, 2017, at 2:44 PM, David Hart <david@hartbit.com> wrote:

On 9 Feb 2017, at 20:43, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

Sent from my iPad

On Feb 9, 2017, at 1:30 PM, Hooman Mehr via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 9, 2017, at 10:47 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:
On Feb 9, 2017, at 4:26 AM, Step Christopher via swift-evolution <swift-evolution@swift.org> wrote:
Looks good. Minor comments below:
The typealias 'T5' is repeated as both an initial composition, and as a demonstration of combining typealiases.

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. They are four solutions to this dilemna:
Do nothing.
Replace all uses of AnyObject by class, breaking source compatibility.
Replace all uses of class by AnyObject, breaking source compatibility.
Redefine AnyObject as typealias AnyObject = class.

I agree with other comments on recommending 4 here, and covering the others as alternatives

I agree that we need the typealias for compatibility. I think it's still worth discussing whether the `AnyObject` typealias should *only* be there for compatibility; it could be deprecated or obsoleted in Swift 4 or future language versions.

I think it might be worth keeping to provide a more sensible capitalization alternative than lower case “class” when used as a type name:

var obj: class // this looks weird because of capitalization.

var obj: AnyObject // this looks better.

I agree that it looks better and would choose AnyObject if source compatibility weren't an issue. One option that wasn't listed was to drop 'class' but use a multi-release deprecation strategy and a fix-it to facilitate a smooth transition. If the community is willing to adopt this approach it would be my first choice.

You mean option 3?

Pretty much, but option 3 does not make it clear that it won’t break source immediately in Swift 4. I think it becomes much more reasonable if Swift 3.1 code still compiles in Swift 4 mode, but with a deprecation warning.

The reason I prefer `AnyObject` to `class` is because I think it’s ugly to have `class` as the name of an existential type. Type names are uppercase in Swift. It is also used to compose with protocols which also use uppercase names in Swift. Because it appears in contexts which use an uppercase convention it makes sense for this to have an uppercase name. `AnyObject` seems like the obvious choice if we’re going to go in that direction.

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

_______________________________________________
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) #18

Why can’t we completely ban this?

protocol A {}
protocol B {}
typealias AB = A & B

protocol C : AB {} // Allowed, but could be also banned

protocol D : A & B {} // Error

I didn’t even know the last one there was banned. /me hangs head in shame.

I think either both should be supported, or neither one should be supported. I’m leaning toward the former :slight_smile:

However note that unlike protocols that inherit from classes, this does not create any conceptual difficulties in the language; it’s merely a syntactic quirk. I’m more concerned about banning protocols that inherit from typealiases that contain classes.

Slava

···

On Feb 14, 2017, at 1:30 AM, Adrian Zubarev <adrian.zubarev@devandartist.com> wrote:

--
Adrian Zubarev
Sent with Airmail

Am 14. Februar 2017 um 10:25:43, Slava Pestov via swift-evolution (swift-evolution@swift.org <mailto:swift-evolution@swift.org>) schrieb:

On Feb 12, 2017, at 12:32 PM, David Hart via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi Matthew,

Your arguments made sense to me. I modified the proposal to choose strategy number 3: deprecating and removing class over several versions to favour AnyObject. Mind having another proof read?

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

Anybody has counter arguments?

Class and Subtype existentials
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#introduction>Introduction

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

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

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

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

id<Protocol1, Protocol2>
Base<Protocol>*
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#proposed-solution>Proposed solution

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

AnyObject & Protocol1 & Protocol2
Base & Protocol
As in Objective-C, the first line is an existential of classes which conform to Protocol1 and Protocol2, and the second line is an existential of subtypes of Base which conform to Protocol.

Here are the new proposed rules for what is valid in a existential conjunction syntax:

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#1-the-first-element-in-the-protocol-composition-syntax-can-be-the-anyobject-keyword-to-enforce-a-class-constraint>1. The first element in the protocol composition syntax can be the AnyObject keyword to enforce a class constraint:

protocol P {}
struct S : P {}
class C : P {}
let t: P & AnyObject // Compiler error: AnyObject requirement must be in first position
let u: AnyObject & P = S() // Compiler error: S is not of class type
let v: AnyObject & P = C() // Compiles successfully
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#2-the-first-element-in-the-protocol-composition-syntax-can-be-a-class-type-to-enforce-the-existential-to-be-a-subtype-of-the-class>2. The first element in the protocol composition syntax can be a class type to enforce the existential to be a subtype of the class:

protocol P {}
struct S {}
class C {}
class D : P {}
class E : C, P {}
let t: P & C // Compiler error: subclass constraint must be in first position
let u: S & P // Compiler error: S is not of class type
let v: C & P = D() // Compiler error: D is not a subtype of C
let w: C & P = E() // Compiles successfully
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#3-when-a-protocol-composition-type-contains-a-typealias-the-validity-of-the-type-is-determined-using-the-following-steps>3. When a protocol composition type contains a typealias, the validity of the type is determined using the following steps:

Expand the typealias
Normalize the type by removing duplicate constraints and replacing less specific constraints by more specific constraints (a class constraint is less specific than a class type constraint, which is less specific than a constraint of a subclass of that class).
Check that the type does not contain two class-type constraints

You could generalize this and instead say that if the type contains two class-type constraints, the resulting existential type is the common base class of the two classes, or AnyObject if they do not share a common base class.

Also, I’d like to see some discussion about class-constrained existentials appearing in the inheritance clause of a protocol. IMHO, we should ban this:

typealias MyType = SomeClass & SomeProtocol

protocol SomeOtherProtocol : MyType {}

Slava

class C {}
class D : C {}
class E {}
protocol P1 {}
protocol P2 {}
typealias TA1 = AnyObject & P1
typealias TA2 = AnyObject & P2
typealias TA3 = C & P2
typealias TA4 = D & P2
typealias TA5 = E & P2

typealias TA5 = TA1 & TA2
// Expansion: typealias TA5 = AnyObject & P1 & AnyObject & P2
// Normalization: typealias TA5 = AnyObject & P1 & P2
// TA5 is valid

typealias TA6 = TA1 & TA3
// Expansion: typealias TA6 = AnyObject & P1 & C & P2
// Normalization (AnyObject < C): typealias TA6 = C & P1 & P2
// TA6 is valid

typealias TA7 = TA3 & TA4
// Expansion: typealias TA7 = C & P2 & D & P2
// Normalization (C < D): typealias TA7 = D & P2
// TA7 is valid

typealias TA8 = TA4 & TA5
// Expansion: typealias TA8 = D & P2 & E & P2
// Normalization: typealias TA8 = D & E & P2
// TA8 is invalid because the D and E constraints are incompatible
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#class-and-anyobject>class and AnyObject

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. To get rid of the duplication, we suggest only keeping AnyObject around. To reduce source-breakage to a minimum, class could be redefined as typealias class = AnyObject and give a deprecation warning on class for the first version of Swift this proposal is implemented in. Later, class could be removed in a subsequent version of Swift.

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

This change will not break Swift 3 compability mode because Objective-C types will continue to be imported as before. But in Swift 4 mode, all types bridged from Objective-C which use the equivalent Objective-C existential syntax could break 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 mode 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 continues to compile but still crashs if the Objective-C code calls a method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code will be imported in Swift 4 mode as:

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

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

An alternative solution to the class/AnyObject duplication was to keep both, redefine AnyObject as typealias AnyObject = class and favor the latter when used as a type name.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#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.

On 9 Feb 2017, at 21:50, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

On Feb 9, 2017, at 2:44 PM, David Hart <david@hartbit.com <mailto:david@hartbit.com>> wrote:

On 9 Feb 2017, at 20:43, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Sent from my iPad

On Feb 9, 2017, at 1:30 PM, Hooman Mehr via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 9, 2017, at 10:47 AM, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 9, 2017, at 4:26 AM, Step Christopher via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Looks good. Minor comments below:
The typealias 'T5' is repeated as both an initial composition, and as a demonstration of combining typealiases.

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. They are four solutions to this dilemna:
Do nothing.
Replace all uses of AnyObject by class, breaking source compatibility.
Replace all uses of class by AnyObject, breaking source compatibility.
Redefine AnyObject as typealias AnyObject = class.

I agree with other comments on recommending 4 here, and covering the others as alternatives

<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#source-compatibility>I agree that we need the typealias for compatibility. I think it's still worth discussing whether the `AnyObject` typealias should *only* be there for compatibility; it could be deprecated or obsoleted in Swift 4 or future language versions.

I think it might be worth keeping to provide a more sensible capitalization alternative than lower case “class” when used as a type name:

var obj: class // this looks weird because of capitalization.

var obj: AnyObject // this looks better.

I agree that it looks better and would choose AnyObject if source compatibility weren't an issue. One option that wasn't listed was to drop 'class' but use a multi-release deprecation strategy and a fix-it to facilitate a smooth transition. If the community is willing to adopt this approach it would be my first choice.

You mean option 3?

Pretty much, but option 3 does not make it clear that it won’t break source immediately in Swift 4. I think it becomes much more reasonable if Swift 3.1 code still compiles in Swift 4 mode, but with a deprecation warning.

The reason I prefer `AnyObject` to `class` is because I think it’s ugly to have `class` as the name of an existential type. Type names are uppercase in Swift. It is also used to compose with protocols which also use uppercase names in Swift. Because it appears in contexts which use an uppercase convention it makes sense for this to have an uppercase name. `AnyObject` seems like the obvious choice if we’re going to go in that direction.

-Joe
_______________________________________________
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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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


(Matthew Johnson) #19

Why can’t we completely ban this?

protocol A {}
protocol B {}
typealias AB = A & B

protocol C : AB {} // Allowed, but could be also banned

protocol D : A & B {} // Error

I didn’t even know the last one there was banned. /me hangs head in shame.

I think either both should be supported, or neither one should be supported. I’m leaning toward the former :slight_smile:

I agree.

However note that unlike protocols that inherit from classes, this does not create any conceptual difficulties in the language; it’s merely a syntactic quirk. I’m more concerned about banning protocols that inherit from typealiases that contain classes.

In my mind, allowing a protocol to “inherit” from a class would be the same as a supertype requirements. I imagine the following to be roughly equivalent in meaning:

protocol Foo {}
func takesFoo<F: Foo where F: MyClass>(foo: F) {}

protocol Foo: MyClass {}
func takesFoo<F: Foo>(foo: F) {}

All we’re doing is moving the supertype constraint to the protocol itself rather than requiring it to be repeated everywhere the protocol is used. What conceptual difficulty is involved in allowing this? Maybe I’m missing something, but I just don’t see it.

A second example that builds on the subtype existential syntax:

protocol Foo {}
typealias Bar = MyClass & Bar

func takesBar<B: Bar>(bar: B) {}

A third example using the typealias in creating a new nominal protocol:

protocol Three: Bar {}

would be equivalent to

protocol Three: MyClass, Foo {}

If we don’t allow this we are not preventing users from adding supertype constraints to a protocol. We’re just requiring them to expose a protocol *without* the supertype requirement and use `&` (maybe with a typealias) everywhere they actually mention the protocol in their interface. This seems a little bit fragile and boilerplate-y. What is the downside of allowing a protocol that is intended to always have a supertype constraint from stating it directly in the declaration of the protocol?

···

On Feb 14, 2017, at 3:57 AM, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 14, 2017, at 1:30 AM, Adrian Zubarev <adrian.zubarev@devandartist.com <mailto:adrian.zubarev@devandartist.com>> wrote:

Slava

--
Adrian Zubarev
Sent with Airmail

Am 14. Februar 2017 um 10:25:43, Slava Pestov via swift-evolution (swift-evolution@swift.org <mailto:swift-evolution@swift.org>) schrieb:

On Feb 12, 2017, at 12:32 PM, David Hart via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi Matthew,

Your arguments made sense to me. I modified the proposal to choose strategy number 3: deprecating and removing class over several versions to favour AnyObject. Mind having another proof read?

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

Anybody has counter arguments?

Class and Subtype existentials
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#introduction>Introduction

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

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

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

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

id<Protocol1, Protocol2>
Base<Protocol>*
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#proposed-solution>Proposed solution

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

AnyObject & Protocol1 & Protocol2
Base & Protocol
As in Objective-C, the first line is an existential of classes which conform to Protocol1 and Protocol2, and the second line is an existential of subtypes of Base which conform to Protocol.

Here are the new proposed rules for what is valid in a existential conjunction syntax:

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#1-the-first-element-in-the-protocol-composition-syntax-can-be-the-anyobject-keyword-to-enforce-a-class-constraint>1. The first element in the protocol composition syntax can be the AnyObject keyword to enforce a class constraint:

protocol P {}
struct S : P {}
class C : P {}
let t: P & AnyObject // Compiler error: AnyObject requirement must be in first position
let u: AnyObject & P = S() // Compiler error: S is not of class type
let v: AnyObject & P = C() // Compiles successfully
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#2-the-first-element-in-the-protocol-composition-syntax-can-be-a-class-type-to-enforce-the-existential-to-be-a-subtype-of-the-class>2. The first element in the protocol composition syntax can be a class type to enforce the existential to be a subtype of the class:

protocol P {}
struct S {}
class C {}
class D : P {}
class E : C, P {}
let t: P & C // Compiler error: subclass constraint must be in first position
let u: S & P // Compiler error: S is not of class type
let v: C & P = D() // Compiler error: D is not a subtype of C
let w: C & P = E() // Compiles successfully
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#3-when-a-protocol-composition-type-contains-a-typealias-the-validity-of-the-type-is-determined-using-the-following-steps>3. When a protocol composition type contains a typealias, the validity of the type is determined using the following steps:

Expand the typealias
Normalize the type by removing duplicate constraints and replacing less specific constraints by more specific constraints (a class constraint is less specific than a class type constraint, which is less specific than a constraint of a subclass of that class).
Check that the type does not contain two class-type constraints

You could generalize this and instead say that if the type contains two class-type constraints, the resulting existential type is the common base class of the two classes, or AnyObject if they do not share a common base class.

Also, I’d like to see some discussion about class-constrained existentials appearing in the inheritance clause of a protocol. IMHO, we should ban this:

typealias MyType = SomeClass & SomeProtocol

protocol SomeOtherProtocol : MyType {}

Slava

class C {}
class D : C {}
class E {}
protocol P1 {}
protocol P2 {}
typealias TA1 = AnyObject & P1
typealias TA2 = AnyObject & P2
typealias TA3 = C & P2
typealias TA4 = D & P2
typealias TA5 = E & P2

typealias TA5 = TA1 & TA2
// Expansion: typealias TA5 = AnyObject & P1 & AnyObject & P2
// Normalization: typealias TA5 = AnyObject & P1 & P2
// TA5 is valid

typealias TA6 = TA1 & TA3
// Expansion: typealias TA6 = AnyObject & P1 & C & P2
// Normalization (AnyObject < C): typealias TA6 = C & P1 & P2
// TA6 is valid

typealias TA7 = TA3 & TA4
// Expansion: typealias TA7 = C & P2 & D & P2
// Normalization (C < D): typealias TA7 = D & P2
// TA7 is valid

typealias TA8 = TA4 & TA5
// Expansion: typealias TA8 = D & P2 & E & P2
// Normalization: typealias TA8 = D & E & P2
// TA8 is invalid because the D and E constraints are incompatible
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#class-and-anyobject>class and AnyObject

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. To get rid of the duplication, we suggest only keeping AnyObject around. To reduce source-breakage to a minimum, class could be redefined as typealias class = AnyObject and give a deprecation warning on class for the first version of Swift this proposal is implemented in. Later, class could be removed in a subsequent version of Swift.

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

This change will not break Swift 3 compability mode because Objective-C types will continue to be imported as before. But in Swift 4 mode, all types bridged from Objective-C which use the equivalent Objective-C existential syntax could break 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 mode 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 continues to compile but still crashs if the Objective-C code calls a method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code will be imported in Swift 4 mode as:

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

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

An alternative solution to the class/AnyObject duplication was to keep both, redefine AnyObject as typealias AnyObject = class and favor the latter when used as a type name.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#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.

On 9 Feb 2017, at 21:50, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

On Feb 9, 2017, at 2:44 PM, David Hart <david@hartbit.com <mailto:david@hartbit.com>> wrote:

On 9 Feb 2017, at 20:43, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Sent from my iPad

On Feb 9, 2017, at 1:30 PM, Hooman Mehr via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 9, 2017, at 10:47 AM, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 9, 2017, at 4:26 AM, Step Christopher via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Looks good. Minor comments below:
The typealias 'T5' is repeated as both an initial composition, and as a demonstration of combining typealiases.

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. They are four solutions to this dilemna:
Do nothing.
Replace all uses of AnyObject by class, breaking source compatibility.
Replace all uses of class by AnyObject, breaking source compatibility.
Redefine AnyObject as typealias AnyObject = class.

I agree with other comments on recommending 4 here, and covering the others as alternatives

<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#source-compatibility>I agree that we need the typealias for compatibility. I think it's still worth discussing whether the `AnyObject` typealias should *only* be there for compatibility; it could be deprecated or obsoleted in Swift 4 or future language versions.

I think it might be worth keeping to provide a more sensible capitalization alternative than lower case “class” when used as a type name:

var obj: class // this looks weird because of capitalization.

var obj: AnyObject // this looks better.

I agree that it looks better and would choose AnyObject if source compatibility weren't an issue. One option that wasn't listed was to drop 'class' but use a multi-release deprecation strategy and a fix-it to facilitate a smooth transition. If the community is willing to adopt this approach it would be my first choice.

You mean option 3?

Pretty much, but option 3 does not make it clear that it won’t break source immediately in Swift 4. I think it becomes much more reasonable if Swift 3.1 code still compiles in Swift 4 mode, but with a deprecation warning.

The reason I prefer `AnyObject` to `class` is because I think it’s ugly to have `class` as the name of an existential type. Type names are uppercase in Swift. It is also used to compose with protocols which also use uppercase names in Swift. Because it appears in contexts which use an uppercase convention it makes sense for this to have an uppercase name. `AnyObject` seems like the obvious choice if we’re going to go in that direction.

-Joe
_______________________________________________
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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
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


(Matthew Johnson) #20

Hi Matthew,

Your arguments made sense to me. I modified the proposal to choose strategy number 3: deprecating and removing class over several versions to favour AnyObject. Mind having another proof read?

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

Anybody has counter arguments?

Class and Subtype existentials

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#introduction>Introduction

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

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

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

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

id<Protocol1, Protocol2>
Base<Protocol>*
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#proposed-solution>Proposed solution

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

AnyObject & Protocol1 & Protocol2
Base & Protocol
As in Objective-C, the first line is an existential of classes which conform to Protocol1 and Protocol2, and the second line is an existential of subtypes of Base which conform to Protocol.

Here are the new proposed rules for what is valid in a existential conjunction syntax:

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#1-the-first-element-in-the-protocol-composition-syntax-can-be-the-anyobject-keyword-to-enforce-a-class-constraint>1. The first element in the protocol composition syntax can be the AnyObject keyword to enforce a class constraint:

protocol P {}
struct S : P {}
class C : P {}
let t: P & AnyObject // Compiler error: AnyObject requirement must be in first position
let u: AnyObject & P = S() // Compiler error: S is not of class type
let v: AnyObject & P = C() // Compiles successfully
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#2-the-first-element-in-the-protocol-composition-syntax-can-be-a-class-type-to-enforce-the-existential-to-be-a-subtype-of-the-class>2. The first element in the protocol composition syntax can be a class type to enforce the existential to be a subtype of the class:

protocol P {}
struct S {}
class C {}
class D : P {}
class E : C, P {}
let t: P & C // Compiler error: subclass constraint must be in first position
let u: S & P // Compiler error: S is not of class type
let v: C & P = D() // Compiler error: D is not a subtype of C
let w: C & P = E() // Compiles successfully
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#3-when-a-protocol-composition-type-contains-a-typealias-the-validity-of-the-type-is-determined-using-the-following-steps>3. When a protocol composition type contains a typealias, the validity of the type is determined using the following steps:

Expand the typealias
Normalize the type by removing duplicate constraints and replacing less specific constraints by more specific constraints (a class constraint is less specific than a class type constraint, which is less specific than a constraint of a subclass of that class).
Check that the type does not contain two class-type constraints

You could generalize this and instead say that if the type contains two class-type constraints, the resulting existential type is the common base class of the two classes, or AnyObject if they do not share a common base class.

But if they share a common base class, the existential is invalid. Did I misunderstand your generalization?

Did you mean to say if they *don’t* share a common base class?

···

On Feb 14, 2017, at 5:28 AM, David Hart <david@hartbit.com> wrote:
On 14 Feb 2017, at 10:24, Slava Pestov <spestov@apple.com <mailto:spestov@apple.com>> wrote:

On Feb 12, 2017, at 12:32 PM, David Hart via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Also, I’d like to see some discussion about class-constrained existentials appearing in the inheritance clause of a protocol. IMHO, we should ban this:

typealias MyType = SomeClass & SomeProtocol

protocol SomeOtherProtocol : MyType {}

Yep, I'll make that clear. It should be disallowed IMHO. Thanks!

Slava

class C {}
class D : C {}
class E {}
protocol P1 {}
protocol P2 {}
typealias TA1 = AnyObject & P1
typealias TA2 = AnyObject & P2
typealias TA3 = C & P2
typealias TA4 = D & P2
typealias TA5 = E & P2

typealias TA5 = TA1 & TA2
// Expansion: typealias TA5 = AnyObject & P1 & AnyObject & P2
// Normalization: typealias TA5 = AnyObject & P1 & P2
// TA5 is valid

typealias TA6 = TA1 & TA3
// Expansion: typealias TA6 = AnyObject & P1 & C & P2
// Normalization (AnyObject < C): typealias TA6 = C & P1 & P2
// TA6 is valid

typealias TA7 = TA3 & TA4
// Expansion: typealias TA7 = C & P2 & D & P2
// Normalization (C < D): typealias TA7 = D & P2
// TA7 is valid

typealias TA8 = TA4 & TA5
// Expansion: typealias TA8 = D & P2 & E & P2
// Normalization: typealias TA8 = D & E & P2
// TA8 is invalid because the D and E constraints are incompatible
<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#class-and-anyobject>class and AnyObject

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. To get rid of the duplication, we suggest only keeping AnyObject around. To reduce source-breakage to a minimum, class could be redefined as typealias class = AnyObject and give a deprecation warning on class for the first version of Swift this proposal is implemented in. Later, class could be removed in a subsequent version of Swift.

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

This change will not break Swift 3 compability mode because Objective-C types will continue to be imported as before. But in Swift 4 mode, all types bridged from Objective-C which use the equivalent Objective-C existential syntax could break 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 mode 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 continues to compile but still crashs if the Objective-C code calls a method of UITableViewDataSource or UITableViewDelegate. But if this proposal is accepted and implemented as-is, the Objective-C code will be imported in Swift 4 mode as:

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

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

An alternative solution to the class/AnyObject duplication was to keep both, redefine AnyObject as typealias AnyObject = class and favor the latter when used as a type name.

<https://github.com/hartbit/swift-evolution/tree/subclass-existentials#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.

On 9 Feb 2017, at 21:50, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

On Feb 9, 2017, at 2:44 PM, David Hart <david@hartbit.com <mailto:david@hartbit.com>> wrote:

On 9 Feb 2017, at 20:43, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Sent from my iPad

On Feb 9, 2017, at 1:30 PM, Hooman Mehr via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 9, 2017, at 10:47 AM, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 9, 2017, at 4:26 AM, Step Christopher via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Looks good. Minor comments below:
The typealias 'T5' is repeated as both an initial composition, and as a demonstration of combining typealiases.

This proposal merges the concepts of class and AnyObject, which now have the same meaning: they represent an existential for classes. They are four solutions to this dilemna:
Do nothing.
Replace all uses of AnyObject by class, breaking source compatibility.
Replace all uses of class by AnyObject, breaking source compatibility.
Redefine AnyObject as typealias AnyObject = class.

I agree with other comments on recommending 4 here, and covering the others as alternatives

<https://github.com/hartbit/swift-evolution/blob/e6411d8a9e7924bbd8a48fc292bf08d58a8d1199/proposals/XXXX-subclass-existentials.md#source-compatibility>I agree that we need the typealias for compatibility. I think it's still worth discussing whether the `AnyObject` typealias should *only* be there for compatibility; it could be deprecated or obsoleted in Swift 4 or future language versions.

I think it might be worth keeping to provide a more sensible capitalization alternative than lower case “class” when used as a type name:

var obj: class // this looks weird because of capitalization.

var obj: AnyObject // this looks better.

I agree that it looks better and would choose AnyObject if source compatibility weren't an issue. One option that wasn't listed was to drop 'class' but use a multi-release deprecation strategy and a fix-it to facilitate a smooth transition. If the community is willing to adopt this approach it would be my first choice.

You mean option 3?

Pretty much, but option 3 does not make it clear that it won’t break source immediately in Swift 4. I think it becomes much more reasonable if Swift 3.1 code still compiles in Swift 4 mode, but with a deprecation warning.

The reason I prefer `AnyObject` to `class` is because I think it’s ugly to have `class` as the name of an existential type. Type names are uppercase in Swift. It is also used to compose with protocols which also use uppercase names in Swift. Because it appears in contexts which use an uppercase convention it makes sense for this to have an uppercase name. `AnyObject` seems like the obvious choice if we’re going to go in that direction.

-Joe
_______________________________________________
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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution