[swift-evolution] [Discussion] Seal `T.Type` into `Type<T>`

Anton Zhilin antonyzhilin at gmail.com
Thu Jul 14 14:01:22 CDT 2016


I didn't send the link to evolution, so here it is:
https://gist.github.com/Anton3/08a069a3b6f634bece7ad666922741d2

Response inline:

2016-07-14 20:52 GMT+03:00 Adrian Zubarev via swift-evolution <
swift-evolution at swift.org>:
>
>
>    -
>
>    There is a small typo (SE–0090 is not accepted yet) - only in the
>    first example:
>
>    func unsafeBitCast<T, U>(_: T, to: U.Type)
>    unsafeBitCast(10, to: Double)
>
>    // The second line should be
>    unsafeBitCast(10, to: Double.self)
>
>
> I'll fix the examples.

>
>    -
>
>    Size and internal structure of Type will be the same as of T.Type
>
>    - Do we really need this to be the same?
>
> Yes, otherwise we cannot remove T.Type. If you want to store *additional*
data in Type<T> and have a reason for that, then why not.

>
>    -
>       -
>
>    Values of Type store identifiers of U such that U: T.
>
>    - Why would we want to store more than one unique identifier?
>
> Another try at explaining my model of Type<T>. Warning: it will be a long
read this time!

During compilation, each type is assigned a unique integer identifier.
Let's suppose there are only three types used in the program: Any is 1,
BaseClass is 2, DerivedClass is 3.

Values of type Type<Any> can contain one of identifiers 1, 2 or 3.
Values of type Type<BaseClass> can contain one of identifiers 2 or 3.
Values of type Type<DerivedClass> can only contain 3.

The same in terms of sets:

Type<Any> = { 1, 2, 3 }
Type<BaseClass> = { 2, 3 }
Type<DerivedClass> = { 3 }

In terms of set theory, type Type<T> contains identifiers of all types U
that are subtypes of T.
If U1, ..., Uk are subtypes of T used in the program, then Type<T> = { T,
U1, ..., Uk }

Example:

let x = Type<MyType>()
let y = Type<MyProtocol>(casting: x)
Type<MyType>.size      //=> 50
Type<MyProtocol>.size  //=> 40
x.size                 //=> 50
y.size                 //=> 50

Again, example with dynamicType. Let's suppose that T = BaseClass and
DerivedClass:
BaseClass.

func dynamicType(_ value: BaseClass) -> Type<BaseClass>

We can't know statically, which type information it returns.
Type<BaseClass> = { 2, 3 }
At runtime, we get to know if value is of BaseClass or of DerivedClass.

In my version, Type<T> should get all capabilities and all syntax of T.Type,
therefore we should be able to drop the latter.

Again, main idea: *rename* T.Type to Type<T>, *maintain* its behaviour and
tweak syntax.

Actually I thought for a while about the negative effect of fully removing
> metatypes from the language. Metatypes allow us to build neat looking
> execution branches like showed in SE–0101.
>
> extension MemoryLayout<T> {
>     init(_ : @autoclosure () -> T) {}
>     public static func of(_ candidate : @autoclosure () -> T) -> MemoryLayout<T>.Type {
>         return MemoryLayout.init(candidate).dynamicType
>     }
> }
>
> // Value
> let x: UInt8 = 5
> MemoryLayout.of(x).size // 1
> MemoryLayout.of(1).size // 8
> MemoryLayout.of("hello").stride // 24
> MemoryLayout.of(29.2).alignment // 8
>
> I wouldn’t want to throw this away.
>
We won't lose literally anything by moving from T.Type to Type<T>.

of returns MemoryLayout<T>.Type, which currently doesn't have size
property. Could you correct your example?

> I played with the idea of keeping T.Type internally but disallow it in
> public declarations. Furthermore metatypes would still exist, but can only
> be instantiated through Type<T>.metatype or Type<T>().metatype.
>
> To keep the neat designing feature but get rid of T.Type we could abuse
> generic typealiases here:
>
> // T.Type would be only visible here but is disallowed in public declarations
> // in favor of `Metatype<T>`
> public typealias Metatype<T> = T.Type
>
> public struct Type<T> : Hashable, CustomStringConvertible, CustomDebugStringConvertible {
>
>>     public var metatype: Metatype<T> { return Type<T>.metatype }
>
>     // Internally `.self` should be renamed to `.metatype` and return
>     // a metatype instance
>     public static var metatype: Metatype<T> { return T.metatype }
>> }
>
> That way the sample from above won’t break from its designing point, but
> will require some refactoring:
>
> extension MemoryLayout<T> {
>     init(_ : @autoclosure () -> T) {}
>     public static func of(_ candidate : @autoclosure () -> T) -> Metatype<MemoryLayout<T>> {
>         return dynamicType(MemoryLayout.init(candidate)).metatype
>     }
> }
>
>  If you wish, Type<T> in my version is rebranded metatype T.Type.

> We should also mention that dynamic casts need some tweaking to work with Type<T>.
>
> In the gist, I suggest to live without tweaking and replace dynamic casts
with failable initializer of Type<T>. That will tweaking syntax of that
casts, but reduce amount of magic.

> And one more thing:
>
> public var size: Int      { get }
> public var stride: Int    { get }
> public var alignment: Int { get }
>
> public static var size: Int      { return Type<T>().size }
> public static var stride: Int    { return Type<T>().stride }
> public static var alignment: Int { return Type<T>().alignment }
>
> Shouldn’t these work exactly the opposite way? If in the future Type<T>
> would be extended with reflection functionality and contain more stored
> properties, it would be lightweight to compute size etc. from static size
> without the need of instantiating the whole type.
>
See example above with sizes.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160714/c1d9a9ef/attachment.html>


More information about the swift-evolution mailing list