[swift-evolution] [Pitch] Refactor Metatypes
Nevin Brackett-Rozinsky
nevin.brackettrozinsky at gmail.com
Wed Sep 28 11:41:50 CDT 2016
Let me first say that this new draft does a great job of explaining the
current situation and the goals of the proposal. The revised “Motivation”
section in particular is very clearly written and introduces the concepts
effectively.
I found the previous versions to be highly confusing, and I did not
understand from them what was being proposed and why. After reading this
one, I feel that I finally comprehend the situation, both in terms of how
metatypes behave today and how the authors would like them to work.
This is still an area where I do not have much expertise, so I look forward
to hearing what better-versed people think of the overall approach as well
as the specific details.
Nevin
On Wed, Sep 28, 2016 at 6:18 AM, Adrian Zubarev via swift-evolution <
swift-evolution at swift.org> wrote:
> Formatted version: https://github.com/DevAndArtist/swift-evolution/
> blob/refactor_metatypes/proposals/0126-refactor-metatypes.md
> ------------------------------
> Refactor Metatypes
>
> - Proposal: SE–0126
> <http://0126-refactor-metatypes-repurpose-t-dot-self-and-mirror.md>
> - Authors: Adrian Zubarev <https://github.com/DevAndArtist>, Anton
> Zhilin <https://github.com/Anton3>, Brent Royal-Gordon
> <https://github.com/brentdax>
> - Status: *Revision*
> - Review manager: Chris Lattner <http://github.com/lattner>
> - Revision: 2
> - Previous Revisions: 1
> <https://github.com/apple/swift-evolution/blob/83707b0879c83dcde778f8163f5768212736fdc2/proposals/0126-refactor-metatypes-repurpose-t-dot-self-and-mirror.md>
>
> Introduction
>
> This proposal removes .Type and .Protocol in favor of two generic-style
> syntaxes and aligns global type(of:) function (SE–0096) to match the
> changes.
>
> Swift-evolution thread (post Swift 3):
>
> - [Pitch] Refactor Metatypes
>
> Older swift-evolution threads: [1]
> <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160718/025115.html>,
> [2]
> <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160718/024772.html>,
> [3]
> <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160704/023818.html>
> Motivation
>
> Every type T has an instance, accessible through T.self, which represents
> the type itself. Like all instances in Swift, this “type instance” itself
> has a type, which is referred to as its “metatype”. The metatype of T is
> written T.Type. The instance members of the metatype are the same as the
> static or class members of the type.
>
> Metatypes have subtype relationships which reflect the types they
> represent. For instance, given these types:
>
> protocol Proto {}
> class Base {}
> class Derived: Base, Proto {}
>
> Derived.Type is a subtype of both Base.Type and Proto.Type (and Any.Type).
> That means that Derived.self can be used anywhere a Derived.Type,
> Base.Type, Proto.Type, or Any.Type is called for.
>
> Unfortunately, this simple picture is complicated by protocols. Proto.self
> is actually of type Proto.Protocol, not type Proto.Type. This is
> necessary because the protocol does not, and cannot, conform to itself; it
> requires conforming types to provide static members, but it doesn’t
> actually provide those members itself. Proto.Type still exists, but it is
> the supertype of all types conforming to the protocol.
>
> Making this worse, a generic type always uses T.Type to refer to the type
> of T.self. So when Proto is bound to a generic parameter P, P.Type is the
> same as Proto.Protocol.
>
> This shifting of types is complicated and confusing; we seek to clean up
> this area.
>
> We also believe that, in the long term, the dot syntax will prevent us
> from implementing certain future enhancements that might be valuable:
>
> - Moving the implementation of metatypes at least partly into the
> standard library.
> - Adding members available on all type instances for features like
> read-write reflection or memory layout information.
> - Conforming metatypes to protocols like Hashable or
> CustomStringConvertible.
> - Offering straightforward syntaxes for dynamic features like looking
> up types by name.
>
> Proposed solution
>
> We abolish .Type and .Protocol in favor of two generic-style syntaxes:
>
> -
>
> Type<T> is the concrete type of T.self. A Type<T> only ever has one
> instance, T.self; even if T has a subtype U, Type<U> is not a subtype
> of Type<T>.
> -
>
> Subtype<T> is the supertype of all Types whose instances are subtypes
> of T, including T itself:
> -
>
> If T is a struct or enum, then Type<T> is the only subtype of
> Subtype<T>.
> -
>
> If T is a class, then Type<T> and the Types of all subclasses of T are
> subtypes of Subtype<T>.
> -
>
> If T is a protocol, then the Types of all concrete types conforming to
> T are subtypes of Subtype<T>. Type<T> is not itself a subtype of
> Subtype<T>, or of any Subtype other than Subtype<Any>.
> -
>
> Structural types follow the subtype/supertype relationships of their
> constituent types. For instance:
> -
>
> Type<(NSString, NSString)> is a subtype of Subtype<(NSObject,
> NSObject)>
> -
>
> Metatypes of functions are a little bit more special (the subtyping
> relation on functions flips around for parameter types
> <https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)>
> ):
> - Type<(Any) -> Void> is a subtype of Subtype<(Int) -> Void> etc.
> - Type<(Void) -> Int> is a subtype of Subtype<(Void) -> Any>
>
> In this new notation, some of our existing standard library functions
> would have signatures like:
>
> func unsafeBitCast<T, U>(_: T, to type: Type<U>) -> U
> func ==(t0: Subtype<Any>?, t1: Subtype<Any>?) -> Bool
> func type<T>(of: T) -> Subtype<T> // SE-0096
>
> That last example, type(of:), is rather interesting, because it is
> actually a magic syntax rather than a function. We propose to align this
> syntax with Type and Subtype by renaming it to Subtype(of:). We believe
> this is clearer about both the type and meaning of the operation.
>
> let anInstance: NSObject = NSString()
> let aClass: Subtype<NSObject> = Subtype(of: anInstance)
>
> print(aClass) // => NSString
>
> More details:
>
> -
>
> Every static or class member of T which can be called on all subtypes
> is an instance member of Subtype<T>. That includes:
> -
>
> Static/class properties and methods
> -
>
> Required initializers (as methods named init)
> -
>
> Unbound instance methods
> -
>
> The Type<T> of a concrete type T has all of the members required by
> Subtype<T>, plus non-required initializers.
> -
>
> The Type<T> of a protocol T includes only unbound instance methods of T
> .
> -
>
> If T conforms to P, then Subtype<T> is a subtype of Subtype<P>, even
> if T is a protocol.
> -
>
> The type of Subtype<T>.self is Type<Subtype<T>>.
> -
>
> The type of Type<T>.self is Type<Type<T>>, which is not a subtype of
> any type except Subtype<Type<T>>. There is an infinite regress of
> Type<...<Type<T>>>s.
> -
>
> Subtypes are abstract types similar to class-bound protocols; they,
> too, support identity operations.
> -
>
> Types are concrete reference types which have identities just like
> objects do.
>
> swift Int.self === Int.self // true Int.self === Any.self // false
>
> *Visual metatype relationship example (not a valid Swift code)*
>
> protocol Foo {
> static func foo()
> func instanceMethodFoo()
> }
>
> protocol Boo : Foo {
> static func foo()
> static func boo()
> func instanceMethodFoo()
> func instanceMethodBoo()
> }
>
> class A : Foo {
> static func foo() { ... }
> func instanceMethodFoo() { ... }
> }
>
> class B : A, Boo {
> static func boo() { ... }
> func instanceMethodBoo() { ... }
> }
>
> /// Swift generates metatypes along the lines of:
> ///
> /// Syntax: `meta protocol Subtype<T>` - only metatypes can conform to these meta protocols
> /// Syntax: `final meta class Type<T>` - metatype
> /// Note: `CapturedType` represents `Self` of `T` in `Subtype<T>`
>
> // For Any:
> meta protocol Subtype<Any> : meta class {
> var `self`: Self { get }
> }
>
> final meta class Type<Any> : Subtype<Any> {
> var `self`: Type<Any> { ... }
> }
>
> // For Foo:
> meta protocol Subtype<Foo> : Subtype<Any> {
> var `self`: Self { get }
> func foo()
> func instanceMethodFoo(_ `self`: CapturedType) -> (Void) -> Void
> }
>
> final meta class Type<Foo> : Subtype<Any> {
> var `self`: Type<Foo> { ... }
> func instanceMethodFoo(_ `self`: Foo) -> (Void) -> Void { ... }
> }
>
> // For Boo:
> meta protocol Subtype<Boo> : Subtype<Foo> {
> var `self`: Self { get }
> func boo()
> func instanceMethodBoo(_ `self`: CapturedType) -> (Void) -> Void
> }
>
> final meta class Type<Boo> : Subtype<Any> {
> var `self`: Type<Boo> { ... }
> func instanceMethodFoo(_ `self`: Boo) -> (Void) -> Void { ... }
> func instanceMethodBoo(_ `self`: Boo) -> (Void) -> Void { ... }
> }
>
> // For A:
> meta protocol Subtype<A> : Subtype<Foo> {
> var `self`: Self { get }
> func foo()
> func instanceMethodFoo(_ `self`: CapturedType) -> (Void) -> Void
> }
>
> final meta class Type<A> : Subtype<A> {
> var `self`: Type<A> { ... }
> func foo() { ... }
> func instanceMethodFoo(_ `self`: A) -> (Void) -> Void { ... }
> }
>
> // For B:
> meta protocol Subtype<B> : Subtype<A>, Subtype<Boo> {
> var `self`: Self
> func foo()
> func boo()
> func instanceMethodFoo(_ `self`: CapturedType) -> (Void) -> Void
> func instanceMethodBoo(_ `self`: CapturedType) -> (Void) -> Void
> }
>
> final meta class Type<B> : Subtype<B> {
> var `self`: Type<B> { ... }
> func foo() { ... }
> func boo() { ... }
> func instanceMethodFoo(_ `self`: B) -> (Void) -> Void { ... }
> func instanceMethodBoo(_ `self`: B) -> (Void) -> Void { ... }
> }
>
> *Some examples*
>
> // Types:
> protocol Foo {}
> protocol Boo : Foo {}
> class A : Foo {}
> class B : A, Boo {}
> struct S: Foo {}
>
> // Metatypes:
> let a1: Type<A> = A.self //=> Okay
> let p1: Type<Foo> = Foo.self //=> Okay
> let p2: Type<Boo> = C.self //=> Error -- `C` is not the same as `Foo`
>
> let any_1: Subtype<Any> = A.self //=> Okay
> let any_2: Subtype<Any> = Foo.self //=> Okay
>
> let a_1: Subtype<A> = A.self //=> Okay
> let p_1: Subtype<Foo> = A.self //=> Okay
> let p_2: Subtype<Foo> = Foo.self //=> Error -- `Type<Foo>` is not a subtype of `Subtype<Foo>`
>
> // Generic functions:
> func dynamic<T>(subtype: Subtype<Any>, `is` _: Type<T>) -> Bool {
> return type is Subtype<T>
> }
>
> func dynamic<T>(subtype: Subtype<Any>, `as` _: Type<T>) -> Subtype<T>? {
> return type as? Subtype<T>
> }
>
> let s1: Type<S> = S.self
>
> dynamic(subtype: s1, is: Foo.self) //=> true
> dynamic(subtype: s1, as: Foo.self) //=> an `Optional<Subtype<Foo>>`
>
> Future Directions
>
> -
>
> We could allow extensions on Type and perhaps on Subtype to add
> members or conform them to protocols. This could allow us to remove some
> standard library hacks, like the non-Equatable-related == operators
> for types.
> -
>
> It may be possible to implement parts of Type as a fairly ordinary
> final class, moving code from the runtime into the standard library.
> -
>
> We could offer a Subtype(ofType: Type<T>, named: String)
> pseudo-initializer which would allow type-safe access to classes by name.
> -
>
> We could offer other reflection and dynamic features on Type and
> Subtype.
> -
>
> We could move the MemoryLayout members into Type (presumably
> prefixed), removing the rather artificial MemoryLayout enum.
> -
>
> Along with other generics enhancements, there may be a use for a
> Subprotocol<T> syntax for any protocol requiring conformance to
> protocol T.
>
> Impact on existing code
>
> This is a source-breaking change that can be automated by a migrator.
>
> We suggest the following migration process; this can differ from the final
> migration process implemented by the core team if this proposal will be
> accepted:
>
> - Any.Type is migrated to Subtype<Any>.
> - If T.Type is in function parameter, where T is a generic type
> parameter, then it’s migrated to Type<T>.
> - Every T.Protocol will be replaced with Type<T>.
> - Every T.Type in a dynamic cast will be replaced with Subtype<T>.
> - If static members are called on a metatype instance, then this
> instance is migrated to Subtype<T>.
> - Return types of functions are migrated to Subtype<T>.
> - Variable declarations is migrated to Subtype<T>.
>
> Alternatives considered
>
> Other names for Type and Subtype were considered:
>
> - Type: SpecificType, Metatype or ExactType.
> - Subtype: Supertype, Base, BaseType, ExistentialType or TypeProtocol.
>
> Alternatively the pseudo initializer Subtype(of:) could remain as a
> global function:
>
> public func subtype<T>(of instance: T) -> Subtype<T>
>
>
>
> --
> Adrian Zubarev
> Sent with Airmail
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160928/839f8c37/attachment.html>
More information about the swift-evolution
mailing list