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

Adrian Zubarev adrian.zubarev at devandartist.com
Fri Jul 15 05:25:03 CDT 2016

Good morning everyone. Welcome to our discussion Brent.

There is nothing bad about Type<T> being a final class. To be honest I played with the idea of Type<T> being a class for optimization purposes, because it does feel like a performance drop if we had to instantiate tons of the same types (which may or may not be really heavy):

func dynamicType<T>(_ instance: T) -> Type<T>

let someHeavyViewController = …
With structs we would do the heavy work over and over again which can decrease our performance.

The reason why I dropped the class was because I though someone might subclass it, and I totally forgot about final.

If we had public final class Type<T> we can optimize the above example and return a reference to the specific Type<T> we already instantiated once.


internal var _typeStore: [Int: Type<Any>] = [:]

extension Type {
    internal static var sharedInstance: Type<T> {
        let identifier = _uniqueIdentifierOf(Type<T>.metatype)
        if let typeReference = Type<T>(casting: _typeStore[identifier]) {
            return typeReference
        let newType = Type<T>()
        // downcast `T` to `Any` but still store `T` in `_underlyingMetatype`
        if let downcastedType = Type<Any>(casting: newType) {
            _typeStore[identifier] = downcastedType
        return newType
Now we can use Type<T>.sharedInstance to instantiate it or just to return an existing instance.

I did some tweaking to the Type<T> api to make this work. I also filed a bug SR–2085 where Metatype<T> does not behave like T.Type which is strange. Furthermore the api uses a workaround T.Metatype (renamed T.Type).

init?(casting: Type<T>?) is now optional, to make sharedInstance work with the dictionary. This change doesn’t hurt at all.

I also implemented a check if T is Any to make downcasting to Any work as expected (T.self is Any.Type is the wrong way to go, because it’s always true no matter what). Took me a few hours to debug and fix these strange bugs.

You can check the (not compiling) gist here: https://gist.github.com/DevAndArtist/a5744f21107812b2d4e6baee2c55e0bf.

If anyone want me to implement a version that does compile, just let me know. It’s already possible, but I’ll have to use .self, .Type instead of .metatype and Metatype<T>.

Adrian Zubarev
Sent with Airmail

Am 15. Juli 2016 um 08:25:40, Brent Royal-Gordon (brent at architechies.com) schrieb:

> On Jul 14, 2016, at 6:33 PM, Anton Zhilin <antonyzhilin at gmail.com> wrote:  
> And such a feature would look odd on a struct.  

But why would it be a struct? Types are obvious candidates to have identities; the type is the same "thing" everywhere it's referred to. Types don't have value semantics; if you perform a mutating operation on a type, that mutation is visible everywhere. Types (at least class types) have subtype relationships; structs can't express those.  

I like the `Type<>` syntax, especially if it's something that could at least partially be written in the standard library (how nice would it be if we had `final class Type<Describing>` in the generated headers?), and I really like the idea of extending a metatype to conform to a protocol. But a lot of what's being discussed here, I just don't get. What's the benefit of switching to structs? Of removing type members?  

> I don't see any problems with Type<Type<T>>. There is finite number of types that are used in the program, and compiler can collect and store information about them all statically. Am I right?  

Maybe not:  

func recursivelyPrint<T>(type: T.Type, depth: Int) {  
guard depth > 0 else { return }  
recursivelyPrint(type: type.dynamicType, depth: depth - 1)  
recursivelyPrint(type: Int.self, depth: 5)  

Brent Royal-Gordon  

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160715/9d6af0e6/attachment.html>

More information about the swift-evolution mailing list