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

Adrian Zubarev adrian.zubarev at devandartist.com
Fri Jul 15 14:24:54 CDT 2016


To summarize a few things that this proposal should change:

Make public .self construct Type<T> via Type<T>.sharedInstance
Let SE–0090 do the rest for public .self drop
Rename type for metatypes T.Type to Metatype<T> (or at least to T.Metatype)

Rename internal .self to .metatype which will return an instance of Metatype<T>

Use Type<T> instead of current T.Type

Implement public func dynamicType<T>(_ instance: T) -> Type<T> using internal _typeStorage. SE–0096

public func dynamicType<T>(_ instance: T) -> Type<T> {
         
    let dynamicMetatype = /* extract dynamic metatype from the `instance` */
    let identifier = _uniqueIdentifierOf(dynamicMetatype)
         
    // Check if the type storage contains an instance of our dynamicType
    if let type = _typeStorage.first(where: { $0.hashValue == identifier }) {
             
        // We only need to switch `Any` to `T` but keep the reference
        // `Type<T>` is implicitly converted to `Type<Type<T>>()`
        return unsafeBitCast(type, to: Type<T>)
    }
         
    // Create and store an instance of `Type<T>`
    var type = Type<T>.sharedInstance
         
    // Check if the identifier for our dynamic metatype is equivalent
    // to the created instance, which implies `dynamicMetatype == T`
    if type.hashValue == identifier {
             
        return type
    }
         
    // If the identifiers are not equivalent then
    // T is probably downcasted from dynamicMetatype
         
    type = Type<Any>(metatype: dynamicMetatype)
    _typeStorage.insert(type)
         
    // `Type<T>` is implicitly converted to `Type<Type<T>>()`
    return unsafeBitCast(type, to: Type<T>)
}
Combine SE–0101



-- 
Adrian Zubarev
Sent with Airmail

Am 15. Juli 2016 um 21:10:01, Adrian Zubarev (adrian.zubarev at devandartist.com) schrieb:

Not sure what you have tried to solve here. sharedInstance works just fine in my test implementation with Xcode 8 beta 2:

Here is a portion of the implementation that does compile and work as expected, at least I couldn’t find any bugs.

WARNING: the implementation we should tackle is here: https://gist.github.com/DevAndArtist/a5744f21107812b2d4e6baee2c55e0bf

It differs how this is done in the following example:

internal func _uniqueIdentifierOf(_ metatype: Any.Type) -> Int {
      
    return ObjectIdentifier.init(metatype).hashValue
}

public typealias Metatype<T> = T.Type
public typealias AnyMetatype = Any.Type

public final class Type<T> : Hashable {
      
    internal let _metatype: AnyMetatype
      
    internal init(metatype: AnyMetatype) {

        // check explicitly if `T` is `Any`
        let tIsAny = _uniqueIdentifierOf(Any.self) == _uniqueIdentifierOf(T.self)
        guard tIsAny || metatype is T.Type else {
            fatalError("'metatype' is not an instace of 'Metatype<T>'")
        }
          
        self._metatype = metatype
    }
      
    internal convenience init() {
          
        self.init(metatype: T.self)
    }
      
    public static func cast<U>(_ optionalType: Type<U>?) -> Type<T>? {
          
        guard let otherType = optionalType else { return nil }
          
        // Check if we can up- or downcast the metatype from `otherType` to `Metatype<T>`
          
        // Bug: SR-2085
        // Workaround: Check explicitly if `T` is `Any`
        //
        let isTAny = _uniqueIdentifierOf(Any.self) == _uniqueIdentifierOf(T.self)
        guard isTAny || otherType._metatype is Metatype<T> else {
            return nil
        }

        return unsafeBitCast(otherType, to: Type<T>.self)
    }
      
    public var metatype: T.Type {
        return unsafeBitCast(self._metatype, to: T.Type.self)
    }
      
    public static var metatype: T.Type { return T.self }
      
    public var hashValue: Int { return _uniqueIdentifierOf(self._metatype) }
}

public func ==<T, U>(lhs: Type<T>, rhs: Type<U>) -> Bool {
    return lhs.hashValue == rhs.hashValue
}

internal var _typeStorage = Set<Type<Any>>()

extension Type {
      
    internal static var sharedInstance: Type<T> {
          
        let identifier = _uniqueIdentifierOf(Type<T>.metatype)
          
        let typeFromStorage = _typeStorage.first(where: { $0.hashValue == identifier })
          
        if let type = Type<T>.cast(typeFromStorage) {
              
            return type
        }
          
        let newType = Type<T>()
          
        // downcast `T` to `Any`
        if let type = Type<Any>.cast(newType) {
              
            _typeStorage.insert(type)
        }
        return newType
    }
}

Type<Int>.sharedInstance
Type<Int>.sharedInstance
Type<Int>.sharedInstance
Type<Int>.sharedInstance

print(_typeStorage) // [Type<Swift.Int>]

Type<String>.sharedInstance

print(_typeStorage) // [Type<Swift.Int>, Type<Swift.String>]

class A {}
class B: A {}

Type<B>.sharedInstance
Type<B>.sharedInstance

print(_typeStorage) // [Type<Swift.Int>, Type<B>, Type<Swift.String>]

Type<A>.sharedInstance

print(_typeStorage) // [Type<Swift.String>, Type<Swift.Int>, Type<A>, Type<B>]


-- 
Adrian Zubarev
Sent with Airmail

Am 15. Juli 2016 um 19:58:28, Anton Zhilin (antonyzhilin at gmail.com) schrieb:

Consider what I said above, I feel like we should tackle this:

drop  
init(_ copy: Type<T>) - to keep equality of  
Type<T> references
Agreed.

make all initializer internal and as exchange make  
sharedInstance public.
This would ensure that you will always get a reference from the type storage, and that there can only exist one for each (dynamic) metatype.

I have another idea:

// No uniqueness guarantee
internal class _Type<T> {
    var _metatype: Metatype<T>

    init()
    init(_: AnyMetatype)
    init?<U>(casting: Type<U>)

    // Instead of a global dictionary
    class var sharedInstance: _Type<T>
}

// Uniqueness guarantee
public struct Type<T> {
    internal var impl: _Type<T>

    public init()
    public init(_: AnyMetatype)
    public init?<U>(casting: Type<U>)
}
I can’t foresee the future, so I’m wondering if the type may mutate somehow when reflections are added?

I don’t think so. If added at all, types should only be able to be created using some kind of builder:

let builder = TypeBuilder(name: "Person", kind: .struct)
builder.addField(name: "", type: Int.self)
//...
let typeRef = builder.create()
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160715/8143f529/attachment.html>


More information about the swift-evolution mailing list