Skip to content

Instantly share code, notes, and snippets.

@Anton3
Last active July 25, 2016 16:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Anton3/29da827cf041f61a1041e416fd03ef82 to your computer and use it in GitHub Desktop.
Save Anton3/29da827cf041f61a1041e416fd03ef82 to your computer and use it in GitHub Desktop.

Refactor metatypes

Introduction

This proposal removes T.Type and T.Protocol introduces Type<T> and Subtype<T> instead. Instances of Type<T> always reflect exact type of T; instances of Subtype<T> reflect subtypes of T, where T is a class or a protocol.

Swift-evolution threads:

Motivation

WIP

Magical members

There were the following "magical" members of all types/instances:

  • .dynamicType, which was replaced with type(of:) function by SE-0096
  • .Type and .Protocol, which we propose to remove, see below
  • .Self, which acts like an associatedtype
  • .self, which will be reviewed in a separate proposal

The tendency is to remove "magical" members: with this proposal there will only be .Self (does not count) and .self.

Also, .Type notation works like a generic type, and giving it generic syntax seems to be a good idea (unification).

Proposed solution

Type<T>

Add Type<T> metatype, instances of which reflect exact type T. It will be commonly used for explicit specialization of generic functions, which is currently done by .Type.

Type<T> will also be used for representing protocols instead of .Protocol. Old .Protocol notation will be removed.

Static functions will not be able to be called on instances of Type<T>, because T can be a protocol and because type T is known at compile time whenever Type<T> is used.

T.self notation will be repurposed to return an instance of Type<T>.

Subtype<T>

Add Subtype<T> metatype, instances of which relect different subtypes of T. Subtype<T> will be commonly used wherever T.Type is currently used to represent dynamic types.

Instances of Subtype<T> cannot reflect protocols. However, Subtype<Any> can reflect any instance of type T, including protocols.

Static functions of type T will be able to be called on instances of Subtype<T>, because T is guaranteed to have implementations for them.

Instances of Subtype<T> will be obtained from Type<T> using explicit or implicit casts.

Examples

protocol P { }
protocol Q : P { }
class A : P { }
class B : A, Q { }

let ex11: Type<P> = P.self  // OK
let ex12: Type<Q> = Q.self  // OK
let ex13: Type<A> = A.self  // OK
let ex14: Type<B> = B.self  // OK

let ex15: Type<P> = Q.self  // Error: P != Q
let ex16: Type<A> = B.self  // Error: A != B
let ex17: Type<P> = ex13    // Error: P != A
let ex18: Type<Q> = ex14    // Error: Q != B

let ex21: Subtype<P> = Q.self  // OK
let ex22: Subtype<A> = B.self  // OK
let ex23: Subtype<P> = ex13    // OK
let ex24: Subtype<Q> = ex14    // OK

let ex25: Subtype<Q> = P.self  // Error: P is not a subtype of Q
let ex26: Subtype<Q> = A.self  // Error: A is not a subtype of Q
let ex27: Subtype<P> = Q.self  // Error: Q is a protocol
let ex28: Subtype<P> = P.self  // Error: P is a protocol

let ex31: Subtype<P> = ex24    // OK, refers to Q
let ex32 = ex24 as Subtype<P>  // OK, refers to Q
let ex33 = ex22 as Subtype<P>  // OK, refers to B
let ex34 = ex33 as Subtype<A>  // Error: a subtype of B might not be a subtype of P

let ex35 = ex33 as? Subtype<A>  // Optional<Subtype<A>>
let ex36 = ex33 as! Subtype<A>  // Subtype<A>
let ex37 = ex32 as! Subtype<A>  // Runtime error
let ex38 = ex32 is  Subtype<A>  // false

let ex41: Type<Any> = Any.self     // OK
let ex42: Type<Any> = A.self       // Error: Any != A
let ex43: Subtype<Any> = A.self    // OK
let ex44: Subtype<Any> = P.self    // OK, special behavior of Metatype<Any>
let ex45: Subtype<Any> = Any.self  // OK, special behavior of Metatype<Any>

Impact on existing code

This is a potentially source-breaking change. However, most common use cases can be migrated safely.

  • Any.Type is migrated to Subtype<Any>
  • If static members are called on a metatype instance, then this instance is migrated to Subtype<T>
  • If T.Type is in function parameter, where T is a generic type parameter, then it's migrated to Type<T>
  • Else T.Type is migrated to Subtype<T>

Alternatives considered

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment