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
+45-15
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:
@@ -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 existential 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,52 @@ 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 {}
67
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'
68
79
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
+
protocolP {}
81
+
extensionC: P {}
70
82
83
+
let pObject: anyAnyObject& P =C() // okay
84
+
```
71
85
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.
73
87
74
-
Examples
88
+
The existential metatype, i.e. `P.Type`, becomes `any P.Type`:
75
89
76
90
```swift
77
91
protocolP {}
78
-
classC {}
92
+
structS: P {}
93
+
94
+
let existentialMetatype: any P.Type= S.self
79
95
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
96
+
protocolQ {}
97
+
extensionS: Q {}
87
98
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
+
protocolP {}
89
108
90
-
functest<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'
91
112
```
92
113
93
114
## Source compatibility
@@ -109,8 +130,17 @@ None.
109
130
110
131
## Alternatives considered
111
132
133
+
### Rename `Any` and `AnyObject`
134
+
112
135
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
136
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.
0 commit comments