SE-0126: Refactor Metatypes, repurpose `T.self` and `Mirror`
* Proposal: [SE-0126](0126-refactor-metatypes-repurpose-t-dot-self-and-mirror.md) * Authors: [Adrian Zubarev](https://github.com/DevAndArtist), [Anton Zhilin](https://github.com/Anton3) * Review Manager: [Chris Lattner](https://github.com/lattner) * Status: **Withdrawn** * Decision Notes: [Rationale](https://forums.swift.org/t/withdrawn-for-revision-se-0126-refactor-metatypes-repurpose-t-self-and-mirror/3499)
Introduction
This proposal wants to revise metatypes T.Type, repurpose public T.self notation to return a new Type<T> type instance rather than a metatype, merge SE-0101 into Type<T>, rename the global function from SE-0096 to match the changes of this proposal and finally rename current Mirror type to introduce a new (lazy) Mirror type.
Swift-evolution threads:
- [\[Proposal\] Refactor Metatypes, repurpose T[dot]self and Mirror](https://forums.swift.org/t/proposal-refactor-metatypes-repurpose-t-dot-self-and-mirror/3460)
- [\[Discussion\] Seal
T.TypeintoType<T>](https://forums.swift.org/t/discussion-seal-t-type-into-type-t/3340) - [\[Discussion\] Can we make
.TypeHashable?](https://forums.swift.org/t/discussion-can-we-make-type-hashable/3232)
GitHub Gist thread:
Motivation
The following tasks require metatype-like types:
- Explicit specialization of functions and expressing specific static types.
- Dynamic dispatch of
staticmethods. - Representing any value as a tree, for debug purposes.
- Retrieving and passing around information about dynamic types - Reflection.
Current state of things:
[1] is given to metatypes T.Type:
The metatype instance is usually ignored. For example, if you pass Derived.self as Base.self into function taking T.Type, it will work with Base. * This raises concerns: are metatypes perfectly suited for that purpose?
[2] is also given to metatypes T.Type:
* Because they are used so often, it's tempting to add useful methods to them, but we can't, because metatypes are not extensible types.
[3] is given to Mirror:
Does its name reflect what it's intended to do? Mirror.DisplayStyle contains optional and set as special cases, but does not contain function at all. Mirror collects all information possible at initialization, while for "true" reflection we want laziness. Mirror allows customization. For example, Array<T> is represented with a field for each of its elements. Do we want this for "true" reflection we want to add in the future?
[4] is given to both metatypes T.Type and Mirror:
Metatypes are generic. But do we want genericity in reflection? No, we almost always want to cast to Any.Type. Metatypes are used for getting both static and dynamic sizes. In this context, distinction between generic parameter T and value of metatype instance is unclear. People are confused that Mirror is intended to be used for full-featured reflection, while it does not aim for that.
Known issues of metatypes:
Assume this function that checks if an Int type conforms to a specific protocol. This check uses current model of metatypes combined in a generic context:
func intConforms<T>(to _: T.Type) -> Bool {
return Int.self is T.Type
}
intConforms(to: CustomStringConvertible.self) //=> FALSE[1] When
Tis a protocolP,T.Typeis the metatype of the protocol type itself,P.Protocol.Int.selfis notP.self.[2] There isn't a way to generically expression
P.Typeyet.[3] The syntax would have to be changed in the compiler to get something that behaves like
.Typetoday.Written by Joe Groff: [\[1\]](https://twitter.com/jckarter/status/754420461404958721) [\[2\]](https://twitter.com/jckarter/status/754420624261472256) [\[3\]](https://twitter.com/jckarter/status/754425573762478080)
A possible workaround might look like the example below, but does not allow to decompose P.Type which is a major implementation problem of this proposal:
func intConforms<T>(to _: T.Type) -> Bool {
return Int.self is T
}
intConforms(to: CustomStringConvertible.Type.self) //=> TRUEThis issue was first found and documented as a strange issue in SR-2085. It also raises the concerns: do we need .Protocol at all?
We can extend this issue and find the second problem by checking against the metatype of Any:
func intConforms<T>(to _: T.Type) -> Bool {
return Int.self is T
}
intConforms(to: Any.Type.self) //=> TRUE
intConforms(to: Any.self) //=> TRUEAs you clearly can see, when using Any the compiler does not require .Type at all.
The third issue will show itself whenever we would try to check protocol relationship with another protocol. Currently there is no way (that we know of) to solve this problem:
protocol P {}
protocol R : P {}
func rIsSubtype<T>(of _: T.Type) -> Bool {
return R.self is T
}
rIsSubtype(of: P.Type.self) //=> FALSEWe also believe that this issue is also the reason why the current global functions sizeof, strideof and alignof make use of generic <T>(: T.Type) declaration notation instead of (: Any.Type).
Proposed solution
### `Metatype<T>`:
* Revise metatypes in generic context so the old `T.Type` notation does not produce `T.Protocol` when a protocol metatype is passed around.
* Introduce a distinction between **public** and **internal** `T.self` notation where the **internal** `T.self` notation will be renamed to `T.metatype`.
* Rename old metatype `T.Type` notation to `Metatype<T>`.
* Make **internal** `T.metatype` notation (builtin - not visible in public Swift) return an instance of `Metatype<T>`.
* Public construction of metatypes will look like `Type<T>.metatype` or `T.self.metatype`, see below.
* Metatypes will be used **only** for dynamic dispatch of static methods, see example below:
```swift
protocol HasStatic { static func staticMethod() -> String; init() }
struct A : HasStatic { static func staticMethod() -> String { return "I am A" }; init() {} }
struct B : HasStatic { static func staticMethod() -> String { return "I am B" }; init() {} }
func callStatic(_ metatype: Metatype<HasStatic>) {
let result = metatype.staticMethod()
print(result)
let instance = metatype.init()
print(instance)
}
let a = Type<A>.metatype
let b = Type<B>.metatype
callStatic(a) //=> "I am A" "A()"
callStatic(b) //=> "A am B" "B()"
```
### `Type<T>` API:
`T.self` will be repurposed to return ab instance of `Type<T>` that is declared as follows:
```swift
public struct Type<T> : Hashable, CustomStringConvertible, CustomDebugStringConvertible {
/// Creates an instance that reflects `T`.
/// Example: `let type = T.self`
public init()
/// Returns the contiguous memory footprint of `T`.
///
/// Does not include any dynamically-allocated or "remote" storage.
/// In particular, `Type<T>.size`, when `T` is a class type, is the
/// same regardless of how many stored properties `T` has.
public static var size: Int { get }
/// Returns the least possible interval between distinct instances of
/// `T` in memory. The result is always positive.
public static var stride: Int { get }
/// Returns the default memory alignment of `T`.
public static var alignment: Int { get }
/// Returns an instance of `Metatype<T>` from captured `T` literal.
public static var metatype: Metatype<T> { get }
/// Returns the contiguous memory footprint of `T`.
///
/// Does not include any dynamically-allocated or "remote" storage.
/// In particular, `Type<T>().size`, when `T` is a class type, is the
/// same regardless of how many stored properties `T` has.
public var size: Int { get }
/// Returns the least possible interval between distinct instances of
/// `T` in memory. The result is always positive.
public var stride: Int { get }
/// Returns the default memory alignment of `T`.
public var alignment: Int { get }
/// Returns an instance of `Metatype<T>` from captured `T` literal.
public var metatype: Metatype<T> { get }
/// Hash values are not guaranteed to be equal across different executions of
/// your program. Do not save hash values to use during a future execution.
public var hashValue: Int { get }
/// A textual representation of `self`.
public var description: String { get }
/// A textual representation of `self`, suitable for debugging.
public var debugDescription: String { get }
}
public func ==<T>(lhs: Type<T>, rhs: Type<T>) -> Bool
```
Size of `Type<T>` struct equals `0`. It will be used for generic function specialization:
```swift
func performWithType(_ type: Type<T>)
performWithType(Float.self)
```
### `metatype(of:)` function:
The global `type(of:)` function from **SE-0096** will be renamed to `metatype(of:)` and receive the following declaration:
```swift
/// Returns a dynamic instance of `Metatype<T>`. A dynamic
/// metatype can reflect type `U` where `U : T`.
public func metatype<T>(of instance: T) -> Metatype<T>
```
### `Mirror` API:
Rename current `Mirror` (Swift 2.2) to `DebugRepresentation` and `CustomReflectable` to `CustomDebugRepresentable`.
A *completely different* `Mirror` type will be introduced in Swift 3.
* `Mirror` wraps metatypes and allows checking subtype relationships at runtime.
* `Mirror` contains dynamic versions of `size`, `stride` and `alignment`.
* Size of `Mirror` itself is always `8` bytes, because it only needs to store a single metatype.
* `Mirror` provides a starting point for adding fully functional (lazy) reflection in the future.
```swift
public struct Mirror : Hashable, CustomStringConvertible, CustomDebugStringConvertible {
/// Creates an instance of `Mirror`, reflecting type, which is
/// reflected by a metatype.
public init(_ metatype: Metatype<Any>)
/// Creates an instance of `Mirror`, reflecting type `T`
public init<T>(_ type: Type<T>)
/// Creates an instance of `Mirror`, reflecting
/// dynamic metatype of a given instance.
public init<T>(reflecting instance: T)
/// Returns the contiguous memory footprint of reflected metatype.
public var size: Int { get }
/// Returns the least possible interval between distinct instances of
/// the dynamic type in memory calculated from the reflected dynamic
/// metatype. The result is always positive.
public var stride: Int { get }
/// Returns the minimum memory alignment of the reflected dynamic
/// metatype.
public var alignment: Int { get }
/// Returns an instance of `Metatype<Any>` from reflected dynamic metatype.
public var metatype: Metatype<Any> { get }
/// Checks if type reflected by `self` is a subtype of type reflected by another `Mirror`.
public func `is`(_ mirror: Mirror) -> Bool { get }
/// Checks if type reflected by `self` is a subtype of `T`.
public func `is`<T>(_ type: Type<T>) -> Bool { get }
/// Checks if type reflected by `self` is a subtype of type reflected by a metatype.
public func `is`<T>(_ metatype: Metatype<T>) -> Bool { get }
/// Hash values are not guaranteed to be equal across different executions of
/// your program. Do not save hash values to use during a future execution.
public var hashValue: Int { get }
/// A textual representation of `self`.
public var description: String { get }
/// A textual representation of `self`, suitable for debugging.
public var debugDescription: String { get }
}
public func ==(lhs: Mirror, rhs: Mirror) -> Bool
```
### Summary of metatype-like types:
#### Before
* `T.Type` does three things:
1. Specialization of functions.
2. Dynamic dispatch of static methods.
3. Partial reflection using dynamic casts and functions like `sizeof`, `strideof` etc.
* `Mirror` does two things:
1. It is primarily intended for use in debugging, like `PlaygroundQuickLook`.
2. With less success, it can be used for reflection.
#### After
* `Type<T>` does specialization of functions.
* `Mirror` does reflection.
* `Metatype<T>` does dynamic dispatch of static methods.
* `DebugRepresentation` is used in debugging.Detailed design
### Possible Implementation:
```swift
public struct Type<T> : Hashable, CustomStringConvertible, CustomDebugStringConvertible {
/// Creates an instance that reflects `T`.
/// Example: `let type = T.self`
public init() {}
/// Returns the contiguous memory footprint of `T`.
///
/// Does not include any dynamically-allocated or "remote" storage.
/// In particular, `Type<T>.size`, when `T` is a class type, is the
/// same regardless of how many stored properties `T` has.
public static var size: Int { return _size(of: T.metatype) }
/// Returns the least possible interval between distinct instances of
/// `T` in memory. The result is always positive.
public static var stride: Int { return _stride(of: T.metatype) }
/// Returns the default memory alignment of `T`.
public static var alignment: Int { return _alignment(of: T.metatype) }
/// Returns an instance of `Metatype<T>` from captured `T` literal.
public static var metatype: Metatype<T> { return T.metatype }
/// Returns the contiguous memory footprint of `T`.
///
/// Does not include any dynamically-allocated or "remote" storage.
/// In particular, `Type<T>().size`, when `T` is a class type, is the
/// same regardless of how many stored properties `T` has.
public var size: Int { return Type<T>.size }
/// Returns the least possible interval between distinct instances of
/// `T` in memory. The result is always positive.
public var stride: Int { return Type<T>.stride }
/// Returns the default memory alignment of `T`.
public var alignment: Int { return Type<T>.alignment }
/// Returns an instance of `Metatype<T>` from captured `T` literal.
public var metatype: Metatype<T> { return Type<T>.metatype }
/// Hash values are not guaranteed to be equal across different executions of
/// your program. Do not save hash values to use during a future execution.
public var hashValue: Int { return _uniqueIdentifier(for: self.metatype) }
/// A textual representation of `self`.
public var description: String { return "Type<\(self.metatype)>()" }
/// A textual representation of `self`, suitable for debugging.
public var debugDescription: String {
return "[" + self.description
+ " metatype: \(self.metatype)"
+ " size: \(self.size)"
+ " stride: \(self.stride)"
+ " alignment: \(self.alignment)]"
}
}
public func ==<T>(lhs: Type<T>, rhs: Type<T>) -> Bool { return true }
/// Returns a dynamic instance of `Metatype<T>`. A dynamic
/// metatype can reflect type `U` where `U : T`.
public func metatype<T>(of instance: T) -> Metatype<T> {
return /* implement */
}
public struct Mirror : Hashable, CustomStringConvertible, CustomDebugStringConvertible {
/// Storage for any dynamic metatype.
internal let _metatype: Metatype<Any>
/// Creates an instance of `Mirror`, reflecting type, which is
/// reflected by a metatype.
public init(_ metatype: Metatype<Any>) {
self._metatype = metatype
}
/// Creates an instance of `Mirror`, reflecting type `T`
public init<T>(_ type: Type<T>) {
self._metatype = type.metatype
}
/// Creates an instance of `Mirror`, reflecting
/// dynamic type of a given instance.
public init<T>(reflecting instance: T) {
self._metatype = metatype(of: instance)
}
/// Returns the contiguous memory footprint of reflected metatype.
public var size: Int { return _size(of: self._metatype) }
/// Returns the least possible interval between distinct instances of
/// the dynamic type in memory calculated from the reflected dynamic
/// metatype. The result is always positive.
public var stride: Int { return _stride(of: self._metatype) }
/// Returns the minimum memory alignment of the reflected dynamic
/// metatype.
public var alignment: Int { return _alignment(of: self._metatype) }
/// Returns an instance of `Metatype<T>` from reflected dynamic metatype.
public var metatype: Any.Type { return self._metatype }
/// Checks if type reflected by `self` is a subtype of type reflected by another `Mirror`.
public func `is`(_ mirror: Mirror) -> Bool {
return _is(metatype: self._metatype, also: mirror.metatype)
}
/// Checks if type reflected by `self` is a subtype of `T`.
public func `is`<T>(_ type: Type<T>) -> Bool {
return _is(metatype: self._metatype, also: type.metatype)
}
/// Checks if type reflected by `self` is a subtype of type reflected by a metatype.
public func `is`<T>(_ metatype: Metatype<T>) -> Bool {
return _is(metatype: self._metatype, also: metatype)
}
/// Hash values are not guaranteed to be equal across different executions of
/// your program. Do not save hash values to use during a future execution.
public var hashValue: Int { return _uniqueIdentifier(for: self._metatype) }
/// A textual representation of `self`.
public var description: String { return "Mirror(\(self._metatype))" }
/// A textual representation of `self`, suitable for debugging.
public var debugDescription: String {
return "[" + self.description
+ " metatype: \(self._metatype)"
+ " size: \(self.size)"
+ " stride: \(self.stride)"
+ " alignment: \(self.alignment)]"
}
}
public func ==(lhs: Mirror, rhs: Mirror) -> Bool {
return lhs.hashValue == rhs.hashValue
}
```
### Internal functions:
These functions were used in the implementation above to calculate metatype related information.
* `_size(of:)`, `_stride(of:)` and `_alignment(of:)` functions need some additional tweaking so they will work with any matatype stored in an instance of `Metatype<Any>` rather than a dynamic `<T>(of metatype: Metatype<T>)` variant, which is not suitable for calculations needed in `Mirror`.
* `_uniqueIdentifier(for:)` function is fully implemented and should just work when the current generic issue with `.Protocol` metatypes is resolved.
* `_is(metatype:also:)` relies on the resolved `.Protocol` issue. The final implementation should allow to check type relationship between two different metatype instances.
```swift
internal func _size(of metatype: Metatype<Any>) -> Int {
// Fix this to allow any metatype
return Int(Builtin.sizeof(metatype))
}
internal func _stride(of metatype: Metatype<Any>) -> Int {
// Fix this to allow any metatype
return Int(Builtin.strideof_nonzero(metatype))
}
internal func _alignment(of metatype: Metatype<Any>) -> Int {
// Fix this to allow any metatype
return Int(Builtin.alignof(metatype))
}
internal func _uniqueIdentifier(for metatype: Metatype<Any>) -> Int {
let rawPointerMetatype = unsafeBitCast(metatype, to: Builtin.RawPointer.metatype)
return Int(Builtin.ptrtoint_Word(rawPointerMetatype))
}
internal func _is(metatype m1: Metatype<Any>, also m2: Metatype<Any>) -> Bool {
return /* implement - checks type ralationship `M1 : M2` and `M1 == M2` */
}
```
### Summary of Steps:
* Revise metatypes in generic context so the old `T.Type` notation does not produce `T.Protocol` when a protocol metatype is passed around.
* Make **public** `T.self` notation return an instance of `Type<T>`.
* Rename **internal** `T.self` notation to `T.metatype` (builtin - not visible in public Swift).
* Rename old metatype `T.Type` notation to `Metatype<T>`.
* Make **internal** `T.metatype` notation return an instance of `Metatype<T>`.
* Revise APIs with current `T.Type` notation to use `Type<T>` and in few edge cases `Metatype<T>`.
* Move `size`, `stride` and `alignment` from **SE-0101** to `Type<T>`.
* Provide a concrete declaration for **SE-0096** and rename it to `metatype(of:)`.
* Rename current `Mirror` type (Swift 2.2) to `DebugRepresentation` and `CustomReflectable` to `CustomDebugRepresentable`.
* Introduce a new `Mirror` type that is intended to replace metatypes for most use cases and extended with reflection in a future release.Impact on existing code
This is a source-breaking change that can be automated by a migrator.
The following steps reflects our suggestion of the migration process; these can differ from the final migration process implemented by the core team if this proposal will be accepted:
T.Type→Metatype<T>T.self→Type<T>.metatypeMirror→DebugRepresentationCustomReflectable→CustomDebugRepresentable
* customMirror → customDebugRepresentation
sizeof(T.self)→Type<T>.sizesizeof(metatype)→Mirror(metatype).size
Migrating metatype variables to use Type<T> and Mirror
Metatype<T> is a safe default for transition, but we want to discourage usage of metatypes. In some cases, we can provide fix-its to replace usage of Metatype<T> with Type<T> or Mirror.
To change type of a variable named type from Metatype<T> to Type<T>:
- Replace its type with
Type<T>. - Use the migration patterns below.
- If some use case does not match any of these, the variable cannot be migrated to type
Type<T>.
Migration patterns:
type = T.self.metatype→type = T.selftype = U.self.metatypewhereU != T→ Automatic migration impossibletype = Type<T>.metatype→type = T.selftype = Type<U>.metatypewhereU != T→ Automatic migration impossibletype = otherMetatypewhereotherMetatype: Metatype<T>→type = T.selftype = otherMetatypewhereotherMetatype: Metatype<U>,U != T→ Automatic migration impossibletype = mirror.metatypewheremirror: Mirror→ Automatic migration impossibleotherMetatype = typewhereotherMetatype: Metatype<U>→otherMetatype = Type<T>.metatypeMirror(type)→Mirror(type)type as otherMetatypewhereotherMetatype: Metatype<U>→type.metatype as metatype<U>type as? otherMetatype→ Automatic migration impossibletype as! otherMetatype→ Automatic migration impossibletype is otherMetatype→ Automatic migration impossible
How to change type of a variable named type from Metatype<T> to Mirror:
- Replace its type with
Mirror. - Use the migration patterns below.
- If some use case does not match any of these, the variable cannot be migrated to type
Mirror.
Migration patterns:
type: Metatype<T>→type: Mirrortype = U.self.metatype→type = Mirror(U.self)type = Type<U>.metatype→type = Mirror(U.self)type = otherMetatype→type = Mirror(otherMetatype)type = mirror.metatypewheremirror: Mirror→type = mirrorotherMetatype = type→otherMetatype = type.metatypeMirror(type)→typetype as otherMetatype→type.metatype as! otherMetatypetype as? otherMetatype→type.metatype as? otherMetatypetype as! otherMetatype→type.metatype as! otherMetatypetype is otherMetatype→type.is(otherMetatype)
We can also migrate metatype parameters of a function, where assignment means passing an argument to that function.
In two cases we can apply these automatically:
- If a generic function takes parameter
Metatype<T>, then we can try to replaceMetatype<T>withType<T>. - We can try to replace usage of
Metatype<Any>(akaAnyMetatype) withMirror.
Alternatives considered
- After refactoring metatypes it is assumed that any metatype can be stored inside an instance of
Metatype<Any>. If that will not be the case, then we propose to introduce a new standalone type for explained behavior. That type could be named asAnyMetatype. Therefore, any type marked withMetatype<Any>in this proposal will becomeAnyMetatype.
- If the community and the core team are strongly against the repurposing of
Mirrorwe'd like to consider to merge the proposed functionality into a single type. For such a change we do believeType<T>might be the right type here. However, this introduces further complications such as storing dynamic metatypes inside ofType<T>and a few other that we don't want go in detail here.
Future directions
Remove public .self:
When SE-0090 is accepted we will remove T.self notation and only have type literals like T.
Examples:
let someInstance = unsafeBitCast(1.0, to: Int)
let dynamicSize = Mirror(reflecting: someInstance).sizeThen we can add Type(_: Type<T>) initializer for disambiguation:
Int.self.size // Works fine with this proposal, but what if we drop `.self`?
Int.size // Will be an error after dropping `.self`.
Type<Int>().size // Would work, but looks odd.
Type(Int).size // This version looks much better.When combined with this proposal, the result will be to eliminate all 'magical' members that existed in the language:
.dynamicType.Type.self
There is also Self, but it acts like an associatedtype.
Extend Mirror with reflection functionality:
Reflection is one of stated goals for Swift 4. With this proposal, adding reflection becomes as simple as extending Mirror. For example, we could add the following computed property:
typealias FieldDescriptor = (name: String, type: Mirror, getter: (Any) -> Any, setter: (inout Any, Any) -> ())
var fields: [FieldDescriptor] { get }