Skip to content

Commit db33b0b

Browse files
committed
Update 'any' proposal based on feedback from the pitch discussion.
1 parent 077b100 commit db33b0b

File tree

1 file changed

+45
-15
lines changed

1 file changed

+45
-15
lines changed

proposals/NNNN-existential-any.md

+45-15
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
Existential types in Swift have an extremely lightweight spelling: a plain protocol name in type context means an existential type. Over the years, this has risen to the level of **active harm** by causing confusion, leading programmers down the wrong path that often requires them to re-write code once they hit a fundamental [limitation of value-level abstraction](https://forums.swift.org/t/improving-the-ui-of-generics/22814#heading--limits-of-existentials). This proposal makes the impact of existential types explicit in the language by annotating such types with `any`.
1212

13+
Swift evolution discussion thread: [[Pitch] Introduce existential `any`](https://forums.swift.org/t/pitch-introduce-existential-any/53520).
14+
1315
## Motivation
1416

1517
Existential types in Swift have significant limitations and performance implications. Some of their limitations are missing language features, but many are fundamental to their type-erasing semantics. For example, given a protocol with associated type requirements, the existential type cannot conform to the protocol itself without a manual conformance implementation, because there is not an obvious concrete associated type that works for any value conforming to the protocol, as shown by the following example:
@@ -51,7 +53,7 @@ existential-type -> 'any' type
5153

5254
### Semantics of explicit existential types
5355

54-
The semantics of `any` types are the same as existential types today. Explicit `any` can only be applied to protocols and protocol compositions; `any` cannot be applied to nominal types, structural types, and type parameters:
56+
The semantics of `any` types are the same as existential types today. Explicit `any` can only be applied to protocols and protocol compositions, or existential metatypes thereof; `any` cannot be applied to nominal types, structural types, type parameters, and protocol metatypes:
5557

5658
```swift
5759
struct S {}
@@ -61,33 +63,52 @@ let s: any S = S() // error: 'any' has no effect on concrete type 'S'
6163
func generic<T>(t: T) {
6264
let x: any T = t // error: 'any' has no effect on type parameter 'T'
6365
}
66+
67+
let f: any ((Int) -> Void) = generic // error: 'any' has no effect on concrete type '(Int) -> Void'
6468
```
6569

66-
`any` also cannot be applied to `Any` and `AnyObject` (unless part of a protocol composition).
70+
`any` is unnecessary for `Any` and `AnyObject` (unless part of a protocol composition):
71+
72+
```swift
73+
struct S {}
74+
class C {}
6775

76+
let value: any Any = S() // warning: 'any' is redundant on type 'Any'
77+
let values: [any Any] = [] // warning: 'any' is redundant on type 'Any'
78+
let object: any AnyObject = C() // warning: 'any' is redundant on type 'AnyObject'
6879

69-
> Rationale: `any Any` and `any AnyObject` are redundant. `Any` and `AnyObject` are already special types in the language, and their existence isn’t nearly as harmful as existential types for regular protocols because the type-erasing semantics is already explicit in the name.
80+
protocol P {}
81+
extension C: P {}
7082

83+
let pObject: any AnyObject & P = C() // okay
84+
```
7185

72-
The existential metatype becomes `(any P).Type`, and the protocol metatype remains `P.Protocol`.
86+
> **Rationale**: `any Any` and `any AnyObject` are redundant. `Any` and `AnyObject` are already special types in the language, and their existence isn’t nearly as harmful as existential types for regular protocols because the type-erasing semantics is already explicit in the name.
7387
74-
Examples
88+
The existential metatype, i.e. `P.Type`, becomes `any P.Type`:
7589

7690
```swift
7791
protocol P {}
78-
class C {}
92+
struct S: P {}
93+
94+
let existentialMetatype: any P.Type = S.self
7995

80-
any P
81-
any Any // error
82-
any AnyObject // error
83-
any P & AnyObject
84-
any C // error
85-
any P & C
86-
any () -> Void // error
96+
protocol Q {}
97+
extension S: Q {}
8798

88-
(any P).Type
99+
let compositionMetatype: any (P & Q).Type = S.self
100+
```
101+
102+
> **Rationale**: The existential metatype erases the underlying concrete metatype and allows access to static protocol requirements. The spelling `(any P).Type` could easily be confused with the metatype of the existential type itself (also called the "protocol metatype"), i.e. the type of `(any P).self`. This value does _not_ have a concrete underlying conforming type through which to invoke static protocol requirements. The spelling `any P.Type` is less likely to cause this confusion, it enforces the notion that the existential metatype is also a form of existential type with type-erasing semantics, and it introduces more syntactic distance between the existential metatype and the protocol metatype.
103+
104+
The protocol metatype and its value, i.e. `P.Protocol` and `P.self`, become `(any P).Protocol` and `(any P).self`, respectively:
105+
106+
```swift
107+
protocol P {}
89108

90-
func test<T>(_: T) where T == any P
109+
let protocolMetatype: (any P).Protocol = (any P).self
110+
let invalidMetatype: (any P).Type = (any P).self // error: metatype of 'any P' is spelled with '.Protocol'
111+
let invalidProtocolMetatype: any P.Protocol = (any P).self // error: protocol metatype is spelled '(any P).Protocol'
91112
```
92113

93114
## Source compatibility
@@ -109,8 +130,17 @@ None.
109130

110131
## Alternatives considered
111132

133+
### Rename `Any` and `AnyObject`
134+
112135
Instead of leaving `Any` and `AnyObject` in their existing spelling, an alternative is to spell these types as `any Value` and `any Object`, respectively. Though this is more consistent with the rest of the proposal, this change would have an even bigger source compatibility impact. Given that `Any` and `AnyObject` aren’t as harmful as other existential types, changing the spelling isn’t worth the churn.
113136

137+
### Use `Any<P>` instead of `any P`
138+
139+
A common suggestion is to spell existential types with angle brackets on `Any`, e.g. `Any<Hashable>`. However, this syntax is misleading because it appears that `Any` is a generic type, which is confusing to the mental model for 2 reasons:
140+
141+
1. A generic type is something programmers can implement themselves. In reality, existential types are a built-in language feature that would be _very_ difficult to replicate with regular Swift code.
142+
2. This syntax creates the misconception that the underlying concrete type is a generic argument to `Any` that is preserved statically in the existential type. The `P` in `Any<P>` looks like an implicit type parameter with a conformance requirement, but it's not; the underlying type confoming to `P` is erased at compile-time.
143+
114144
## Future Directions
115145

116146
### Extending existential types

0 commit comments

Comments
 (0)