[swift-evolution] [Revision] [Pitch] Rename `T.Type`

Adrian Zubarev adrian.zubarev at devandartist.com
Sat Jul 23 08:19:15 CDT 2016


Updated with changes written by Anton: https://github.com/DevAndArtist/swift-evolution/blob/rename_t_dot_type/proposals/0126-rename-t-dot-type.md

Introduction

This proposal renames T.Type to Metatype<T>, renames type(of:) to metatype(of:) and removes P.Protocol metatypes.

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 into Type<T>
Motivation

Explanation of metatypes
Current behavior of .Protocol

For protocols P, besides normal P.Type, there is also a “restricting metatype” P.Protocol that is the same as P.Type, except that it can only reflect P itself and not any of its subtypes:

Int.self is CustomStringConvertible.Type      //=> true
Int.self is CustomStringConvertible.Protocol  //=> false
Even without P.Protocol, we can test for equality:

Int.self is CustomStringConvertible.Type  //=> true
Int.self == CustomStringConvertible.self  //=> false
For protocols P, P.self returns a P.Protocol, not P.Type:

let metatype = CustomStringConvertible.self
print(type(of: metatype))  //=> CustomStringConvertible.Protocol
In practise, the existence of P.Protocol creates problems. If T is a generic parameter, then T.Type turns into P.Protocol if a protocol P is passed:

func printMetatype<T>(_ meta: T.Type) {
    print(dynamicType(meta))
    let copy = T.self
    print(dynamicType(copy))
}

printMetatype(CustomStringConvertible.self)  //=> CustomStringConvertible.Protocol x2
Lets review the following situation:

func isIntSubtype<T>(of: T.Type) -> Bool {
    return Int.self is T.Type
}

isIntSubtype(of: CustomStringConvertible.self)  //=> false
Now we understand that because T is a protocol P, T.Type turns into a P.Protocol, and we get the confusing behaviour.

Summing up issues with P.Protocol, it does not bring any additional functionality (we can test .Types for is and for ==), but tends to appear unexpectedly and break subtyping with metatypes.

Even more issues with .Protocol

[1] When T is a protocol P, T.Type is the metatype of the protocol type itself, P.Protocol. Int.self is not P.self.

[2] There isn’t a way to generically expression P.Type yet.

[3] The syntax would have to be changed in the compiler to get something that behaves like .Type today.

Written by Joe Groff: [1] [2] [3]
There is a workaround for isIntSubtype example above. If we pass a P.Type.Type, then it turns into P.Type.Protocol, but it is still represented with .Type in generic contexts. If we manage to drop outer .Type, then we get P.Type:

func isIntSubtype<T>(of _: T.Type) -> Bool {
  return Int.self is T   // not T.Type here anymore
}

isIntSubtype(of: CustomStringConvertible.Type.self) //=> true
In this call, T = CustomStringConvertible.Type. We can extend this issue and find the second problem by checking against the metatype of Any:

func isIntSubtype<T>(of _: T.Type) -> Bool {
    return Int.self is T
}

isIntSubtype(of: Any.Type.self) //=> true

isIntSubtype(of: Any.self)      //=> true
When using Any, the compiler does not require .Type and returns true for both variations.

The third issue shows itself when we try to check protocol relationship with another protocol. Currently, there is no way (that we know of) to solve this problem:

protocol Parent {}
protocol Child : Parent {}

func isChildSubtype<T>(of _: T.Type) -> Bool {
    return Child.self is T
}

isChildSubtype(of: Parent.Type.self) //=> false
We also believe that this issue is the reason why the current global functions sizeof, strideof and alignof make use of generic <T>(_: T.Type) declaration notation instead of (_: Any.Type).

Magical members

There were the following “magical” members of all types/instances:

.dynamicType, which was replaced with type(of:) function by SE–0096.
.Type and .Protocol, which we propose to remove, see below.
.Self, which acts like an associatedtype.
.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).

Proposed solution

Remove P.Protocol type without a replacement. P.self will never return a P.Protocol.
Rename T.Type to Metatype<T>.
Rename type(of:) function from SE–0096 to metatype(of:).
Impact on existing code

This is a source-breaking change that can be automated by a migrator. All occurrences of T.Type and T.Protocol will be changed to Metatype<T>. All usages of type(of:) will be changed to metatype(of:)

Alternatives considered

Rename T.self to T.metatype. However, this can be proposed separately.
Use Type<T> instead of Metatype<T>. However, Metatype is more precise here.


-- 
Adrian Zubarev
Sent with Airmail
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160723/e1b55961/attachment.html>


More information about the swift-evolution mailing list