You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: proposals/NNNN-existential-any.md
+78-16
Original file line number
Diff line number
Diff line change
@@ -10,6 +10,8 @@
10
10
11
11
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`.
12
12
13
+
Swift evolution discussion thread: [[Pitch] Introduce existential `any`](https://forums.swift.org/t/pitch-introduce-existential-any/53520).
14
+
13
15
## Motivation
14
16
15
17
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:
@@ -35,7 +37,7 @@ Despite these significant and often undesirable implications, existential types
35
37
36
38
## Proposed solution
37
39
38
-
I propose to make existential types syntactically explicit in the language using the `any` keyword. This proposal introduces the new syntax, and this syntax should be required under the Swift 6 language mode.
40
+
I propose to make existential types syntactically explicit in the language using the `any` keyword. This proposal introduces the new syntax in the Swift 5 language mode, and this syntax should be required for existential types under the Swift 6 language mode.
39
41
40
42
## Detailed design
41
43
@@ -51,7 +53,7 @@ existential-type -> 'any' type
51
53
52
54
### Semantics of explicit existential types
53
55
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 metatypes thereof; `any` cannot be applied to nominal types, structural types, type parameters, and protocol metatypes:
55
57
56
58
```swift
57
59
structS {}
@@ -61,33 +63,84 @@ let s: any S = S() // error: 'any' has no effect on concrete type 'S'
61
63
funcgeneric<T>(t: T) {
62
64
let x: any T = t // error: 'any' has no effect on type parameter 'T'
63
65
}
66
+
67
+
let f: any ((Int) ->Void) = generic // error: 'any' has no effect on concrete type '(Int) -> Void'
64
68
```
65
69
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
+
structS {}
74
+
classC {}
75
+
76
+
let value: anyAny=S() // warning: 'any' is redundant on type 'Any'
77
+
let values: [anyAny] = [] // warning: 'any' is redundant on type 'Any'
78
+
let object: anyAnyObject=C() // warning: 'any' is redundant on type 'AnyObject'
67
79
80
+
protocolP {}
81
+
extensionC: P {}
68
82
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.
83
+
let pObject: anyAnyObject& P =C() // okay
84
+
```
70
85
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.
71
87
72
-
The existential metatype becomes `(any P).Type`, and the protocol metatype remains `P.Protocol`.
88
+
#### Metatypes
73
89
74
-
Examples
90
+
The existential metatype, i.e. `P.Type`, becomes `any P.Type`. The protocol metatype, i.e. `P.Protocol`, becomes `(any P).Type`. The protocol metatype value `P.self` becomes `(any P).self`:
75
91
76
92
```swift
77
93
protocolP {}
78
-
classC {}
94
+
structS: P {}
95
+
96
+
let existentialMetatype: any P.Type= S.self
97
+
98
+
protocolQ {}
99
+
extensionS: Q {}
100
+
101
+
let compositionMetatype: any (P & Q).Type= S.self
102
+
103
+
let protocolMetatype: (any P).Type= (any P).self
104
+
```
105
+
106
+
> **Rationale**: The existential metatype is spelled `any P.Type` because it's an existential type that is a generalization over metatypes. The protocol metatype is the singleton metatype of the existential type `any P` itself, i.e. `(any P).Type`. Under this model, the `any` keyword conceptually acts like an existential quantifier `∃ T`. `any P.Type` means `∃ T:P . T.Type`, i.e. the metatype of a concrete type conforming to `P`.`(any P).Type` is `(∃ T:P . T).Type`, i.e. the metatype of the existential type itself.
107
+
108
+
#### Type aliases and associated types
109
+
110
+
Like plain protocol names, a type alias to a protocol `P` can be used as both a generic constraint and an existential type. Because `any` is explicitly an existential type, a type alias to `any P` can only be used as an existential type, it cannot be used as a generic conformance constraint, and `any` does not need to be written at the use-site:
111
+
112
+
```swift
113
+
protocolP {}
114
+
typealiasAnotherP= P
115
+
typealiasAnyP=any P
79
116
80
-
any P
81
-
anyAny// error
82
-
anyAnyObject// error
83
-
any P &AnyObject
84
-
any C // error
85
-
any P & C
86
-
any () ->Void// error
117
+
structS: P {}
87
118
88
-
(any P).Type
119
+
let p2: any AnotherP =S()
120
+
let p1: AnyP =S()
89
121
90
-
functest<T>(_: T) where T ==any P
122
+
funcgeneric<T: AnotherP>(value: T) { ... }
123
+
funcgeneric<T: AnyP>(value: T) { ... } // error
124
+
```
125
+
126
+
Once the `any` spelling is required under the Swift 6 language mode, a type alias to a plain protocol name is not a valid type witness for an associated type requirement; existential type witnesses must be explicit in the `typealias` with `any`:
127
+
128
+
```swift
129
+
// Swift 6 code
130
+
131
+
protocolP {}
132
+
133
+
protocolRequirements {
134
+
associatedtypeA
135
+
}
136
+
137
+
structS1: Requirements {
138
+
typealiasA= P // error: associated type requirement cannot be satisfied with a protocol
139
+
}
140
+
141
+
structS2: Requirements {
142
+
typealiasA=any P // okay
143
+
}
91
144
```
92
145
93
146
## Source compatibility
@@ -109,8 +162,17 @@ None.
109
162
110
163
## Alternatives considered
111
164
165
+
### Rename `Any` and `AnyObject`
166
+
112
167
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.
113
168
169
+
### Use `Any<P>` instead of `any P`
170
+
171
+
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:
172
+
173
+
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.
174
+
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 conforming to `P` is erased at compile-time.
0 commit comments