I’m still not fully convinced about fully removing the access to the metatype.

If you look at Ericas and Daves current proposal for MemoryLayout it would 
completely break and it would become impossible to build something like this.

And yes I borrowed the idea of putting properties like size into Type<T> from 
SE–0101. That was mentioned in my previews posts.

If we remove .Type, how’d we access static members in other types like 
MemoryLayout<T>?

This is another contra argument to consider.
@Erica @Dave: Any statement on our conversation about Type<T>?

I apologize that this whole idea raised in parallel to your proposal.

PS: We can merge our ideas into a single formal proposal. Ping me off-list when 
you get some time tomorrow.



-- 
Adrian Zubarev
Sent with Airmail

Am 13. Juli 2016 um 22:57:40, Anton Zhilin (antonyzhi...@gmail.com) schrieb:

An even better explanation of my suggestion: instead of sealing T.Type, I 
suggest to rename it to Type<T>, formally turning it into a struct, and then 
add size and align properties to it.
And regardless of how you interpret it, I suggest to remove .Type notation.

> In Swift, only class instances and metatypes have unique identities. There is 
> no notion of identity for structs, enums, functions, or tuples.
So Type<T> will lose identity. That's fine. They will contain unique integer 
identifiers (assigned to types during compilation) to do its work.

> There is no special compiler magic needed
Maybe. At least, this behaviour will be needed in construction from type 
literals:

func foo(_ x: Type<BaseClass>)
foo(DerivedClass)  // ok
foo(Int)           // compilation error

With Type<T>, I can see reflection coming! For example, Type<T> can have the 
following property (although we shouldn't right now):

var fields: [String: (Type<Any>, (T) -> Any)] { get }

I suggest you to prepare a formal proposal, or I will try tomorrow. I would 
take the direction on removal of T.Type notation.

2016-07-13 22:48 GMT+03:00 Adrian Zubarev via swift-evolution 
<swift-evolution@swift.org>:
Okay I get it now. You meant the size for a metatype sizeof(T.Type.self). This 
is indeed 8 bytes, at least not for optimized Bool (as you showed).

Internally, Type contains an Int, i.e. identifier of a type. For every type, 
compiler must choose a different identifier
We can already implement this with Hashable protocol.

ObjectIdentifier: A unique identifier for a class instance or metatype.

In Swift, only class instances and metatypes have unique identities. There is 
no notion of identity for structs, enums, functions, or tuples.
// version 1:
public let hashValue: Int = ObjectIdentifier(T.self).hashValue

// version 2 (uses ObjectIdentifier calculation without   
// constructing an instance of ObjectIdentifier):

init() {
    // calculate the hashValue only once
    // I'd rename `.self` to `.metatype`
      
    let rawPointerMetatype = unsafeBitCast(T.self, to: Builtin.RawPointer.self)
    self.hashValue = Int(Builtin.ptrtoint_Word(rawPointerMetatype))
}

public let hashValue: Int
API of Type is defined so that it can only contain identifiers of subtypes of T

For example, when you get a variable of Type, it can correspond to BaseClass or 
DerivedClass
I did a quick test and I feel like this falls under the part of tweaking 
dynamic casts to work with Type<T>. There is no special compiler magic needed, 
unsafeBitCast should do the trick. Or we should teach dynamic casts to work 
with the inner type T instead of the whole Type<T>.

public struct Type<T> : Hashable {
      
    public let metatype: T.Type = T.self

    public let hashValue: Int = ObjectIdentifier(T.self).hashValue
}

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

public func asOptionalCast<U, T>(type: Type<T>) -> Type<U>? {
      
    guard (type.metatype as? U.Type) != nil else {
        return nil
    }
    return unsafeBitCast(type, to: Type<U>.self)
}

class A {}
class B: A {}

let typeB = Type<B>()

// downcast Type<B> to Type<A>
let downcast: Type<A>? = asOptionalCast(type: typeB)

(downcast! == typeB) == true

// cast Type<A> (which here is Type<B>) back to Type<B>
let upcast: Type<B>? = asOptionalCast(type: downcast!)

(upcast! == Type<B>()) == true
The good part here that the hash value of the casted type won’t change and 
testing against a new instance of the same dynamic type will be always true.



-- 
Adrian Zubarev
Sent with Airmail

Am 13. Juli 2016 um 20:31:22, Anton Zhilin (antonyzhi...@gmail.com) schrieb:

2016-07-13 20:19 GMT+03:00 Adrian Zubarev via swift-evolution 
<swift-evolution@swift.org>:
Am 13. Juli 2016 um 18:30:53, Anton Zhilin (antonyzhi...@gmail.com) schrieb:

I see model of Type<T> as follows:
Values of Type<T> are identifiers of types (8 bytes, I guess)
All used identifiers are contained in Type<Any>
Type<T> contains a subset of those identifiers
I can’t follow your though here. Is this a totally new behavior?

That's how it works right now:

sizeofValue(Bool.self)              //=> 0 (optimized away)
sizeofValue(Bool.self as Any.self)  //=> 8

I'll put my thoughts another way:
Internally, Type<T> contains an Int, i.e. identifer of a type. For every type, 
compiler must choose a different identifier
API of Type<T> is defined so that it can only contain identifiers of subtypes 
of T
For example, when you get a variable of Type<BaseClass>, it can correspond to 
BaseClass or DerivedClass
Upcasting uses constructor init<U: T>(upcasting: Type<U>)
It does look neat but I can’t implement it. At least I have no clue how to 
solve the problem that `T` is not a protocol or a class type.

We should add implicit convertion of Type<U> to Type<T>, dulicating current 
behaviour of `as T.Type`.
That constructor still can be useful, but it will be failable and not that 
significant in practise.
I don't like adding compiler magic, but I guess we really should in this case, 
because otherwise Type<T> can't replace T.Type.
Size of Type<Type<T>> is 8, static size of Type<SomeProtocol> is 0
I feel like you mean something different here than this:

```swift

protocol SomeProtocol {}

sizeof(SomeProtocol.self) == 40
```

That was a mistake. Static size of Type<SomeProtocol> will be 40, and size 
property of its values can be less or greater than 40, depending on internal 
type identifier.


_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to