- Proposal: SE-0126
- Authors: Adrian Zubarev, Anton Zhilin
- Status: Revision
- Review manager: Chris Lattner
- Revision: 2
- Previous Revisions: 1
This proposal removes T.Type
and T.Protocol
introduces Type<T>
and Subtype<T>
instead. Instances of Type<T>
always reflect exact type of T
; instances of Subtype<T>
reflect subtypes of T
, where T
is a class or a protocol.
Swift-evolution threads:
- [Revision] [Pitch] Rename
T.Type
- [Review] SE-0126: Refactor Metatypes, repurpose T[dot]self and Mirror
- [Proposal] Refactor Metatypes, repurpose T[dot]self and Mirror
- [Discussion] Seal
T.Type
intoType<T>
WIP
There were the following "magical" members of all types/instances:
.dynamicType
, which was replaced withtype(of:)
function by SE-0096.Type
and.Protocol
, which we propose to remove, see below.Self
, which acts like anassociatedtype
.self
, which will be reviewed in a separate proposal
The tendency is to remove "magical" members: with this proposal there will only be .Self
(does not count) and .self
.
Also, .Type
notation works like a generic type, and giving it generic syntax seems to be a good idea (unification).
Add Type<T>
metatype, instances of which reflect exact type T
. It will be commonly used for explicit specialization of generic functions, which is currently done by .Type
.
Type<T>
will also be used for representing protocols instead of .Protocol
. Old .Protocol
notation will be removed.
Static functions will not be able to be called on instances of Type<T>
, because T
can be a protocol and because type T
is known at compile time whenever Type<T>
is used.
T.self
notation will be repurposed to return an instance of Type<T>
.
Add Subtype<T>
metatype, instances of which relect different subtypes of T
. Subtype<T>
will be commonly used wherever T.Type
is currently used to represent dynamic types.
Instances of Subtype<T>
cannot reflect protocols. However, Subtype<Any>
can reflect any instance of type T
, including protocols.
Static functions of type T
will be able to be called on instances of Subtype<T>
, because T
is guaranteed to have implementations for them.
Instances of Subtype<T>
will be obtained from Type<T>
using explicit or implicit casts.
protocol P { }
protocol Q : P { }
class A : P { }
class B : A, Q { }
let ex11: Type<P> = P.self // OK
let ex12: Type<Q> = Q.self // OK
let ex13: Type<A> = A.self // OK
let ex14: Type<B> = B.self // OK
let ex15: Type<P> = Q.self // Error: P != Q
let ex16: Type<A> = B.self // Error: A != B
let ex17: Type<P> = ex13 // Error: P != A
let ex18: Type<Q> = ex14 // Error: Q != B
let ex21: Subtype<P> = Q.self // OK
let ex22: Subtype<A> = B.self // OK
let ex23: Subtype<P> = ex13 // OK
let ex24: Subtype<Q> = ex14 // OK
let ex25: Subtype<Q> = P.self // Error: P is not a subtype of Q
let ex26: Subtype<Q> = A.self // Error: A is not a subtype of Q
let ex27: Subtype<P> = Q.self // Error: Q is a protocol
let ex28: Subtype<P> = P.self // Error: P is a protocol
let ex31: Subtype<P> = ex24 // OK, refers to Q
let ex32 = ex24 as Subtype<P> // OK, refers to Q
let ex33 = ex22 as Subtype<P> // OK, refers to B
let ex34 = ex33 as Subtype<A> // Error: a subtype of B might not be a subtype of P
let ex35 = ex33 as? Subtype<A> // Optional<Subtype<A>>
let ex36 = ex33 as! Subtype<A> // Subtype<A>
let ex37 = ex32 as! Subtype<A> // Runtime error
let ex38 = ex32 is Subtype<A> // false
let ex41: Type<Any> = Any.self // OK
let ex42: Type<Any> = A.self // Error: Any != A
let ex43: Subtype<Any> = A.self // OK
let ex44: Subtype<Any> = P.self // OK, special behavior of Metatype<Any>
let ex45: Subtype<Any> = Any.self // OK, special behavior of Metatype<Any>
This is a potentially source-breaking change. However, most common use cases can be migrated safely.
Any.Type
is migrated toSubtype<Any>
- If static members are called on a metatype instance, then this instance is migrated to
Subtype<T>
- If
T.Type
is in function parameter, whereT
is a generic type parameter, then it's migrated toType<T>
- Else
T.Type
is migrated toSubtype<T>