[swift-evolution] [Proposal] New mechanism to combine Types with/or Protocols

Adrian Zubarev adrian.zubarev at devandartist.com
Mon May 16 12:18:31 CDT 2016


I open a new thread just for clarity. This proposal was started two weeks ago in a discussion thread where I talked to the community and refined the proposal over a few past days. The overall response was positive to this proposal.

Original thread: [Pitch] merge types and protocols back together with type<Type, Protocol, ...>

This is my first proposal where I'll submit a pull request. Furthermore my English isn’t that great so please bear with me. If you spot any typos or other mistakes I’d be happy to look into your feedback. Feel free to send me such feedback in private.

Just for clarification: this proposal does not try to create type intersection in Swift, so please don’t ask me to change this. `Type intersection` is totally a different story and can have its own thread and proposal. ;)

Here is the formatted version of my proposal: https://github.com/DevAndArtist/swift-evolution/blob/master/proposals/nnnn-mechanism-to-combine-types-and-protocols.md

I hope to see your final feedback before I submit the proposal to the evolution repository.

-- 
Adrian Zubarev
Sent with Airmail
New mechanism to combine Types with/or Protocols

Proposal: SE-NNNN
Author(s): Adrian Zubarev
Status: Awaiting review
Review manager: TBD
Introduction

The current protocol<> mechanism defines the Any protocol to which all types implicitly conform. It also can combine distinct protocols to a new Protocol, whereas combinging SubProtocol with its BaseProtocol will be inferred as SubProtocol (the order withhin the angle brackets doesn’t matter). I propose to replace the current protocol<> mechanism with a new more powerful mechanism called All<>, which will allow combining Types with independent Protocols and enforce all constraints.

Swift-evolution thread: [Pitch] Merge Types and Protocols back together with type<Type, Protocol, ...>

Motivation

The motivation for this proposal comes from solving the missing Type issue (rdar://20990743) and combining the idea mentioned in the generics manifesto for Swift 3 in section Renaming protocol<...> to Any<...>.

The proposed mechanism will allow us to create a new Type from extendable distinct types - there are still some restrinctions you can read about below.

Proposed solution

First step to implement this proposal would need to rename protocol<> to All<> and configure the migration process to update code that used old style protocol<>.

Next the All<> mechanism would be extended to allow nesting and at most one extendable value or reference type. When all types within angle brackets are indepented/distinct to each other a new Type will be formed by All<A, B, ...>. This new Type can be used to store instances that conform to all constrains defined by All<> without any generic context (see Detailed design for more information).

Here only some subtype of UIView (dynamic type) conforms to SomeProtocol, but both types within angle brackets are distinct.

protocol SomeProtocol {}
extension SomeProtocol {
    func foo() { print("fooooo") }
}

// we'll be able to store the new type without generics
class A {
    var view: All<UIView, SomeProtocol>
    init(view: All<UIView, SomeProtocol>) {
        self.view = view
    }
     
    // `dynamicType` of the `view` might be `UIButton` for example
    // but we still be able to acces SomeProtocol which only UIButton conforms to
    func doSomeWork() {
        self.view.removeFromSuperview() // just for a simple example
        self.view.foo() // prints "fooooo"
    }
}

extension UIButton: SomeProtocol {}

let button: SomeProtocol = UIButton() // split types for the example

if let mergedValue = button as? All<UIView, SomeProtocol> {
    let a = A(view: mergedValue)
    a.doSomeWork()
}
Detailed design

Rules for All<>:

Empty All<> will be used as typealias Any = All<>. No constraints means it can accept any type or simply that all types implicitly conform to empty All<>. This is the logical replacement for typealias Any = protocol<>.

The order of Types within angle brackets doesn’t matter: All<A, B> == All<B, A>. (The compiler already reorders the types by its own will from protocol<B, A> to protocol<A, B>.)

All<> can be composed from protocols and by the mention of this rule fully replace protocol<...>

All<ProtocolA, ...> equals to old protocol<ProtocolA, ...>
All<ProtocolX> equals to old protocol<ProtocolX> or simply inferred as ProtocolX
All<> can contain at most one extendable value or reference type plus none or n protocols.

All<ReferenceType> or All<ReferenceType, Protocol, ...>
All<ValueType> or All<ValueType, Protocol, ...>
This rule will disallow All<> to contain unnecessary inheritance type branches from subtypeable types.
Furthermore will this rule ban confusion when using All<T, U> in a generic context.
Subtypeable Type from within angle brackets of All<> can be seen as the base type of the dynamic type.

Nesting All<> is allowed under special rules:

A: All<> can contain B: All<> if B is composed from protocols:
e.g. All<AnyType, All<ProtocolA, ProtocolB, ...>> will be inferred as All<AnyType, ProtocolA, ProtocolB, ...>
For subtypeable types A: All<> can contain B: All<> if B is composed from a possible base type of A and none or n protocols:
e.g. A = All<All<C, Protocol>, SomeProtocol> where B = All<C, Protocol> and C is some base Type of A which implies to A = All<C, Protocol, SomeProtocol>
Again nesting the inheritance type branch is not allowed, because of rule #4.
All types should be checked againts each other to find a simplified Type. At the end all type dependencies should be distinct and will form a new constrained type. These constraints are then tested against the dynamic type.

protocol X {}   protocol Y: X {}
class A: X {}   class B: Y {}
     
All<A, X, Y> /* inferred as */ All<A, Y>  
// e.g. will the above type accept `B` but not `A` as long `A` doesn't conform to `Y`
     
All<B, X, Y> /* inferred as */ All<B> /* or simply just */ B  
All<> can be an optional type All<>? or All<>!

Detailed design for All<> (below type is an extendable type):

type A can be applied to All<A> == A, All<Any> == Any == All<> or All<GenericType> == GenericType

type B: C:
class B: ProtocolC can be applied to:

Type	Equivalent inferred Type (** generic)
All<ProtocolC>	ProtocolC
All<Any, ProtocolC>	ProtocolC
All<Any>	Any or All<>
All<GenericType, ProtocolC>	**DynamicType: ProtocolC
All<GenericType>	**DynamicType 
All<B, ProtocolC>	B
All<B>	B
class B: ClassC can be applied to:

One would not want to combine All<ClassC, B> even in a generic context like All<T, U> for example to union one value of B and another value of ClassC, because followed by rule #2 the compiler will reoder the types to All<B, ClassC> and infer that as B. B can not hold ClassC. This implies that All<> does not intersect types and the need of rule #4.
Type	Equivalent inferred Type (** generic)
All<ClassC>	ClassC
All<B>	B
All<GenericType>	**DynamicType
All<Any>	Any or All<>
struct B: ProtocolC is analogous to class B: ProtocolC.

enum B: ProtocolC is analogous to struct B: ProtocolC.

protocol B: C can be applied to:

Type	Equivalent inferred Type (** generic)
All<Any, B>	B
All<Any, C>	C
All<Any>	Any or All<>
All<GenericType, B>	**DynamicType: B
All<GenericType, C>	**DynamicType: C
All<GenericType>	**DynamicType
All<B, C>	B
All<B>	B
All<C>	C
type B distinct to C:
The following points will produce a new Type:
class B and protocol C can be combined to All<B, C>.
struct B and protocol C can be combined to All<B, C>.
enum B and protocol C can be combined to All<B, C>.
protocol B and protocol C can be combined to All<B, C>.
The following points should all raise an compilation error:
class B and class C can NOT be combined.
struct B and struct C can NOT be combined.
enum B and enum C can NOT be combined.
type D: E, F where E doesn’t conform to F:
class D: ClassE, ProtocolF type can be applied to:

Type	Equivalent inferred Type (** generic)
All<ProtocolF>	ProtocolF
All<Any, ProtocolF>	ProtocolF
All<Any>	Any or All<>
All<GenericType, ProtocolF>	**DynamicType: ProtocolF
All<GenericType>	**DynamicType
All<D, ProtocolF>	D
All<D>	D
All<ClassE, ProtocolF>	NEW: All<ClassE, ProtocolF>
All<ClassE>	ClassE
class D: ProtocolE, ProtocolF type can be applied to:

Type	Equivalent inferred Type (** generic)
All<ProtocolE, ProtocolF>	All<ProtocolE, ProtocolF>
All<ProtocolE>	ProtocolE
All<ProtocolF>	ProtocolF
All<Any, ProtocolE, ProtocolF>	All<ProtocolE, ProtocolF>
All<Any, ProtocolE>	ProtocolE
All<Any, ProtocolF>	ProtocolF
All<Any>	Any or All<>
All<GenericType, ProtocolE, ProtocolF>	**DynamicType: All<ProtocolE, ProtocolF>
All<GenericType, ProtocolE>	**DynamicType: ProtocolE
All<GenericType, ProtocolF>	**DynamicType: ProtocolF
All<GenericType>	**DynamicType
All<D, ProtocolE, ProtocolF>	D
All<D, ProtocolE>	D
All<D, ProtocolF>	D
All<D>	D
struct D: ProtocolE, ProtocolF is analogous to class D: ProtocolE, ProtocolF.

enum D: ProtocolE, ProtocolF is analogous to struct D: ProtocolE, ProtocolF.

protocol D: E, F type can be applied to:

Type	Equivalent inferred Type (** generic)
All<GenericType, E, F>	**DynamicType: All<E, F>
All<GenericType, E>	**DynamicType: E
All<GenericType, F>	**DynamicType: F
All<GenericType>	**DynamicType
All<D, E, F>	D
All<D, E>	D
All<D, F>	D
All<E, F>	All<E, F>
All<E>	E
All<F>	F
All<D>	D
Possible functions (just a few examples - solves rdar://problem/15873071 and rdar://20990743):

// with assumed generalized `class` and `AnyObject` typealias in mind
// current verion with unwanted `@objc` and possible bridging  
func woo<T: AnyObject where T: SomeProtocol>(value: T)

// rewritten without generics to accept only classes that conform to `SomeProtocol`
func woo(value: All<AnyObject, SomeProtocol>)

// more specific example which accepts all subtypes of `UIView` that conform  
// to `SomeProtocol` and one would drop generics here  
func woo(value: All<UIView, SomeProtocol>)
Impact on existing code

These changes will break existing code. Projects using old style protocol<> mechanism will need to migrate to the new All<> mechanism. The code using old style protocol<> won’t compile until updated to the new conventions.

Alternatives considered

This feature was orginally proposed as type<> but was renamed to All<> (idea provided by: Thorsten Seitz) to dodge possible confusion and serve its main purpose to enforce multiple/all constraints.

This proposal was overall updated to include any possible overlap with Generics Manifesto for Swift 3. The reason the mechanism in this proposal is called All<> instead of the suggested name Any<> from the manifesto is to reserve that name for future usage explained in Future directions below.

The mechanism still could be named Any<> and the described Any<> in Future directions could be named Either<>.

Any other rule changes for All<> are not considered.

Future directions

Generalize class constraints. This will create the possibility for AnyObject typealias.
typealias AnyObject = All<class> // @objc will be removed

// or  
typealias ClassInstance = All<class>

// and ban confusion with old `AnyClass` vs. `AnyObject`
typealias ClassType = ClassInstance.Type
Adding constraints for other extendable types like struct and enum and generalize these. This change will allo us to form more typealiases like:
typealias AnyStruct = All<struct>
typealias AnyEnum = All<enum>

// or
typealias StructInstance = All<struct>
typealias EnumInstance = All<enum>

// and
typealias StructType = StructInstance.Type
typealias EnumType = EnumInstance.Type
Possible functions (just a few more examples):

// to start with we should remember that we are already to do some syntax  
// magic with current`protocol<>`
protocol A { func zoo() }

// this is the base design that does not violate any rule
func boo(value: All<A>) { value.zoo() }

// this is the refined design that we all use today
func boo(value: A) { value.zoo() }

// we could constrain functions to accept only structs
// this might be seen as an enforcement to the library user to design a  
// struct in this scenario
func foo(value: StructInstance)
// structs that conforms to a specific protocol (from our library?)
func foo(value: All<StructInstance, SomeProtocol>)  
// one could use of ClassInstance and EnumInstance analogically

// generalized way with generics
func foo<T: struct where T: SomeProtocol>(value: T)  
// or
func foo<T: StructInstance where T: SomeProtocol>(value: T)  

// current only one verion for classes with unwanted `@objc` and possible bridging  
func woo<T: AnyObject where T: SomeProtocol>(value: T)
// better alternative might look like
func woo<T: class where T: SomeProtocol>(value: T)  
// or
func woo<T: ClassInstance where T: SomeProtocol>(value: T)  
// `All<>` combine with generics
func woo<T: UIView>(value: All<T, SomeProtocol>)  
// or simpler without generics
func woo(value: All<UIView, SomeProtocol>)

// non-generic approach to accept only reference-types which conforms to `SomeProtocol`
func zoo(value: All<ClassInstance, SomeProtocol>)
// or
func zoo(value: All<class, SomeProtocol>)
Possible scenarios:

// constrainted to accept only structs
struct A<T: struct> {
    var value: T  
}  

// or
struct A<T: StructInstance> {
    var value: T  
}  

protocol SomeProtocol { /* implement something shiny */ }

// lets say this array is filled with structs, classes and enums that conforms to `SomeProtocol`
let array: [SomeProtocol] = // fill

// this would be new
var classArray: [SomeProtocol] = array.filter { $0 is All<class, SomeProtocol> }
var structArray: [SomeProtocol] = array.filter { $0 is All<struct, SomeProtocol> }
var enumArray: [SomeProtocol] = array.filter { $0 is All<enum, SomeProtocol> }
// we still would have to convert these types
Flattened operators or even Type operators for All<>:

class B {
    var mainView: UIView & SomeProtocol
     
    init(view: UIView & SomeProtocol) {
        self. mainView = view
    }
}
The & type operator would produce a “flattened" All<> with its operands. It could be overloaded to accept either a concrete type or a protocol on the lhs and would produce Type for an lhs that is a type and all when lhs is a protocol. Type operators would be evaluated during compile time and would produce a type that is used where the expression was present in the code. This is a long-term idea, not something that needs to be considered right now.

Written by: Matthew Johnson
Adding Any<> or Either<> which can reduce overloading (idea provided by: Thorsten Seitz). Any<> or Either<> will pick the first type match from angle brackets with the dynamic type at compile time and proceed. One would then need to handle the value by own desire.

func foo(value: Any<String, Int>) {
     
    if let v = value as? String {
        // do some work
    } else if let v = value as? Int {
        // do some work
    }
}
     
// flattened version for `Any<>` or `Either<>`
func foo(value: String | Int)
Mix different types like All<> and Any<>:

// accept dynamic type constrained by  
// (`ClassA` AND `SomeProtocol`) OR (`ClassB` AND `SomeProtocol`)
func foo(value: All<Any<ClassA, ClassB>, SomeProtocol>)

// flattened version
func foo(value: (ClassA | ClassB) & SomeProtocol)
Typealias AnyValue or ValueInstance (for extendable types only):

typealias AnyValue = Any<All<struct>, All<enum>> // magic isn't it?
typealias AnyValue = Any<AnyStruct, AnyEnum>

// or
typealias ValueInstance = Any<All<struct>, All<enum>>
typealias ValueInstance = Any<StructInstance, EnumInstance>

// and  
typealias ValueType = ValueInstance.Type

// flattened version
typealias AnyValue = All<struct> | All<enum>
typealias AnyValue = AnyStruct | AnyEnum
typealias ValueInstance = StructInstance | EnumInstance

// any value which conforms to `SomeProtocol`; reference types finally are out the way
func foo<T>(value: All<AnyValue, SomeProtocol>)  
func foo<T>(value: All<ValueInstance, SomeProtocol>)  

// flattened version
func foo<T>(value: AnyValue & SomeProtocol)  
func foo<T>(value: ValueInstance & SomeProtocol)  
New mechanism to combine Types with/or Protocols

Proposal: SE-NNNN
Author(s): Adrian Zubarev
Status: Awaiting review
Review manager: TBD
Introduction

The current protocol<> mechanism defines the Any protocol to which all types implicitly conform. It also can combine distinct protocols to a new Protocol, whereas combinging SubProtocol with its BaseProtocol will be inferred as SubProtocol (the order withhin the angle brackets doesn’t matter). I propose to replace the current protocol<> mechanism with a new more powerful mechanism called All<>, which will allow combining Types with independent Protocols and enforce all constraints.

Swift-evolution thread: [Pitch] Merge Types and Protocols back together with type<Type, Protocol, ...>

Motivation

The motivation for this proposal comes from solving the missing Type issue (rdar://20990743) and combining the idea mentioned in the generics manifesto for Swift 3 in section Renaming protocol<...> to Any<...>.

The proposed mechanism will allow us to create a new Type from extendable distinct types - there are still some restrinctions you can read about below.

Proposed solution

First step to implement this proposal would need to rename protocol<> to All<> and configure the migration process to update code that used old style protocol<>.

Next the All<> mechanism would be extended to allow nesting and at most one extendable value or reference type. When all types within angle brackets are indepented/distinct to each other a new Type will be formed by All<A, B, ...>. This new Type can be used to store instances that conform to all constrains defined by All<> without any generic context (see Detailed design for more information).

Here only some subtype of UIView (dynamic type) conforms to SomeProtocol, but both types within angle brackets are distinct.

protocol SomeProtocol {}
extension SomeProtocol {
    func foo() { print("fooooo") }
}

// we'll be able to store the new type without generics
class A {
    var view: All<UIView, SomeProtocol>
    init(view: All<UIView, SomeProtocol>) {
        self.view = view
    }
     
    // `dynamicType` of the `view` might be `UIButton` for example
    // but we still be able to acces SomeProtocol which only UIButton conforms to
    func doSomeWork() {
        self.view.removeFromSuperview() // just for a simple example
        self.view.foo() // prints "fooooo"
    }
}

extension UIButton: SomeProtocol {}

let button: SomeProtocol = UIButton() // split types for the example

if let mergedValue = button as? All<UIView, SomeProtocol> {
    let a = A(view: mergedValue)
    a.doSomeWork()
}
Detailed design

Rules for All<>:

Empty All<> will be used as typealias Any = All<>. No constraints means it can accept any type or simply that all types implicitly conform to empty All<>. This is the logical replacement for typealias Any = protocol<>.

The order of Types within angle brackets doesn’t matter: All<A, B> == All<B, A>. (The compiler already reorders the types by its own will from protocol<B, A> to protocol<A, B>.)

All<> can be composed from protocols and by the mention of this rule fully replace protocol<...>

All<ProtocolA, ...> equals to old protocol<ProtocolA, ...>
All<ProtocolX> equals to old protocol<ProtocolX> or simply inferred as ProtocolX
All<> can contain at most one extendable value or reference type plus none or n protocols.

All<ReferenceType> or All<ReferenceType, Protocol, ...>
All<ValueType> or All<ValueType, Protocol, ...>
This rule will disallow All<> to contain unnecessary inheritance type branches from subtypeable types.
Furthermore will this rule ban confusion when using All<T, U> in a generic context.
Subtypeable Type from within angle brackets of All<> can be seen as the base type of the dynamic type.

Nesting All<> is allowed under special rules:

A: All<> can contain B: All<> if B is composed from protocols:
e.g. All<AnyType, All<ProtocolA, ProtocolB, ...>> will be inferred as All<AnyType, ProtocolA, ProtocolB, ...>
For subtypeable types A: All<> can contain B: All<> if B is composed from a possible base type of A and none or n protocols:
e.g. A = All<All<C, Protocol>, SomeProtocol> where B = All<C, Protocol> and C is some base Type of A which implies to A = All<C, Protocol, SomeProtocol>
Again nesting the inheritance type branch is not allowed, because of rule #4.
All types should be checked againts each other to find a simplified Type. At the end all type dependencies should be distinct and will form a new constrained type. These constraints are then tested against the dynamic type.

protocol X {}   protocol Y: X {}
class A: X {}   class B: Y {}
     
All<A, X, Y> /* inferred as */ All<A, Y>  
// e.g. will the above type accept `B` but not `A` as long `A` doesn't conform to `Y`
     
All<B, X, Y> /* inferred as */ All<B> /* or simply just */ B  
All<> can be an optional type All<>? or All<>!

Detailed design for All<> (below type is an extendable type):

type A can be applied to All<A> == A, All<Any> == Any == All<> or All<GenericType> == GenericType

type B: C:
class B: ProtocolC can be applied to:

Type	Equivalent inferred Type (** generic)
All<ProtocolC>	ProtocolC
All<Any, ProtocolC>	ProtocolC
All<Any>	Any or All<>
All<GenericType, ProtocolC>	**DynamicType: ProtocolC
All<GenericType>	**DynamicType 
All<B, ProtocolC>	B
All<B>	B
class B: ClassC can be applied to:

One would not want to combine All<ClassC, B> even in a generic context like All<T, U> for example to union one value of B and another value of ClassC, because followed by rule #2 the compiler will reoder the types to All<B, ClassC> and infer that as B. B can not hold ClassC. This implies that All<> does not intersect types and the need of rule #4.
Type	Equivalent inferred Type (** generic)
All<ClassC>	ClassC
All<B>	B
All<GenericType>	**DynamicType
All<Any>	Any or All<>
struct B: ProtocolC is analogous to class B: ProtocolC.

enum B: ProtocolC is analogous to struct B: ProtocolC.

protocol B: C can be applied to:

Type	Equivalent inferred Type (** generic)
All<Any, B>	B
All<Any, C>	C
All<Any>	Any or All<>
All<GenericType, B>	**DynamicType: B
All<GenericType, C>	**DynamicType: C
All<GenericType>	**DynamicType
All<B, C>	B
All<B>	B
All<C>	C
type B distinct to C:
The following points will produce a new Type:
class B and protocol C can be combined to All<B, C>.
struct B and protocol C can be combined to All<B, C>.
enum B and protocol C can be combined to All<B, C>.
protocol B and protocol C can be combined to All<B, C>.
The following points should all raise an compilation error:
class B and class C can NOT be combined.
struct B and struct C can NOT be combined.
enum B and enum C can NOT be combined.
type D: E, F where E doesn’t conform to F:
class D: ClassE, ProtocolF type can be applied to:

Type	Equivalent inferred Type (** generic)
All<ProtocolF>	ProtocolF
All<Any, ProtocolF>	ProtocolF
All<Any>	Any or All<>
All<GenericType, ProtocolF>	**DynamicType: ProtocolF
All<GenericType>	**DynamicType
All<D, ProtocolF>	D
All<D>	D
All<ClassE, ProtocolF>	NEW: All<ClassE, ProtocolF>
All<ClassE>	ClassE
class D: ProtocolE, ProtocolF type can be applied to:

Type	Equivalent inferred Type (** generic)
All<ProtocolE, ProtocolF>	All<ProtocolE, ProtocolF>
All<ProtocolE>	ProtocolE
All<ProtocolF>	ProtocolF
All<Any, ProtocolE, ProtocolF>	All<ProtocolE, ProtocolF>
All<Any, ProtocolE>	ProtocolE
All<Any, ProtocolF>	ProtocolF
All<Any>	Any or All<>
All<GenericType, ProtocolE, ProtocolF>	**DynamicType: All<ProtocolE, ProtocolF>
All<GenericType, ProtocolE>	**DynamicType: ProtocolE
All<GenericType, ProtocolF>	**DynamicType: ProtocolF
All<GenericType>	**DynamicType
All<D, ProtocolE, ProtocolF>	D
All<D, ProtocolE>	D
All<D, ProtocolF>	D
All<D>	D
struct D: ProtocolE, ProtocolF is analogous to class D: ProtocolE, ProtocolF.

enum D: ProtocolE, ProtocolF is analogous to struct D: ProtocolE, ProtocolF.

protocol D: E, F type can be applied to:

Type	Equivalent inferred Type (** generic)
All<GenericType, E, F>	**DynamicType: All<E, F>
All<GenericType, E>	**DynamicType: E
All<GenericType, F>	**DynamicType: F
All<GenericType>	**DynamicType
All<D, E, F>	D
All<D, E>	D
All<D, F>	D
All<E, F>	All<E, F>
All<E>	E
All<F>	F
All<D>	D
Possible functions (just a few examples - solves rdar://problem/15873071 and rdar://20990743):

// with assumed generalized `class` and `AnyObject` typealias in mind
// current verion with unwanted `@objc` and possible bridging  
func woo<T: AnyObject where T: SomeProtocol>(value: T)

// rewritten without generics to accept only classes that conform to `SomeProtocol`
func woo(value: All<AnyObject, SomeProtocol>)

// more specific example which accepts all subtypes of `UIView` that conform  
// to `SomeProtocol` and one would drop generics here  
func woo(value: All<UIView, SomeProtocol>)
Impact on existing code

These changes will break existing code. Projects using old style protocol<> mechanism will need to migrate to the new All<> mechanism. The code using old style protocol<> won’t compile until updated to the new conventions.

Alternatives considered

This feature was orginally proposed as type<> but was renamed to All<> (idea provided by: Thorsten Seitz) to dodge possible confusion and serve its main purpose to enforce multiple/all constraints.

This proposal was overall updated to include any possible overlap with Generics Manifesto for Swift 3. The reason the mechanism in this proposal is called All<> instead of the suggested name Any<> from the manifesto is to reserve that name for future usage explained in Future directions below.

The mechanism still could be named Any<> and the described Any<> in Future directions could be named Either<>.

Any other rule changes for All<> are not considered.

Future directions

Generalize class constraints. This will create the possibility for AnyObject typealias.
typealias AnyObject = All<class> // @objc will be removed

// or  
typealias ClassInstance = All<class>

// and ban confusion with old `AnyClass` vs. `AnyObject`
typealias ClassType = ClassInstance.Type
Adding constraints for other extendable types like struct and enum and generalize these. This change will allo us to form more typealiases like:
typealias AnyStruct = All<struct>
typealias AnyEnum = All<enum>

// or
typealias StructInstance = All<struct>
typealias EnumInstance = All<enum>

// and
typealias StructType = StructInstance.Type
typealias EnumType = EnumInstance.Type
Possible functions (just a few more examples):

// to start with we should remember that we are already to do some syntax  
// magic with current`protocol<>`
protocol A { func zoo() }

// this is the base design that does not violate any rule
func boo(value: All<A>) { value.zoo() }

// this is the refined design that we all use today
func boo(value: A) { value.zoo() }

// we could constrain functions to accept only structs
// this might be seen as an enforcement to the library user to design a  
// struct in this scenario
func foo(value: StructInstance)
// structs that conforms to a specific protocol (from our library?)
func foo(value: All<StructInstance, SomeProtocol>)  
// one could use of ClassInstance and EnumInstance analogically

// generalized way with generics
func foo<T: struct where T: SomeProtocol>(value: T)  
// or
func foo<T: StructInstance where T: SomeProtocol>(value: T)  

// current only one verion for classes with unwanted `@objc` and possible bridging  
func woo<T: AnyObject where T: SomeProtocol>(value: T)
// better alternative might look like
func woo<T: class where T: SomeProtocol>(value: T)  
// or
func woo<T: ClassInstance where T: SomeProtocol>(value: T)  
// `All<>` combine with generics
func woo<T: UIView>(value: All<T, SomeProtocol>)  
// or simpler without generics
func woo(value: All<UIView, SomeProtocol>)

// non-generic approach to accept only reference-types which conforms to `SomeProtocol`
func zoo(value: All<ClassInstance, SomeProtocol>)
// or
func zoo(value: All<class, SomeProtocol>)
Possible scenarios:

// constrainted to accept only structs
struct A<T: struct> {
    var value: T  
}  

// or
struct A<T: StructInstance> {
    var value: T  
}  

protocol SomeProtocol { /* implement something shiny */ }

// lets say this array is filled with structs, classes and enums that conforms to `SomeProtocol`
let array: [SomeProtocol] = // fill

// this would be new
var classArray: [SomeProtocol] = array.filter { $0 is All<class, SomeProtocol> }
var structArray: [SomeProtocol] = array.filter { $0 is All<struct, SomeProtocol> }
var enumArray: [SomeProtocol] = array.filter { $0 is All<enum, SomeProtocol> }
// we still would have to convert these types
Flattened operators or even Type operators for All<>:

class B {
    var mainView: UIView & SomeProtocol
     
    init(view: UIView & SomeProtocol) {
        self. mainView = view
    }
}
The & type operator would produce a “flattened" All<> with its operands. It could be overloaded to accept either a concrete type or a protocol on the lhs and would produce Type for an lhs that is a type and all when lhs is a protocol. Type operators would be evaluated during compile time and would produce a type that is used where the expression was present in the code. This is a long-term idea, not something that needs to be considered right now.

Written by: Matthew Johnson
Adding Any<> or Either<> which can reduce overloading (idea provided by: Thorsten Seitz). Any<> or Either<> will pick the first type match from angle brackets with the dynamic type at compile time and proceed. One would then need to handle the value by own desire.

func foo(value: Any<String, Int>) {
     
    if let v = value as? String {
        // do some work
    } else if let v = value as? Int {
        // do some work
    }
}
     
// flattened version for `Any<>` or `Either<>`
func foo(value: String | Int)
Mix different types like All<> and Any<>:

// accept dynamic type constrained by  
// (`ClassA` AND `SomeProtocol`) OR (`ClassB` AND `SomeProtocol`)
func foo(value: All<Any<ClassA, ClassB>, SomeProtocol>)

// flattened version
func foo(value: (ClassA | ClassB) & SomeProtocol)
Typealias AnyValue or ValueInstance (for extendable types only):

typealias AnyValue = Any<All<struct>, All<enum>> // magic isn't it?
typealias AnyValue = Any<AnyStruct, AnyEnum>

// or
typealias ValueInstance = Any<All<struct>, All<enum>>
typealias ValueInstance = Any<StructInstance, EnumInstance>

// and  
typealias ValueType = ValueInstance.Type

// flattened version
typealias AnyValue = All<struct> | All<enum>
typealias AnyValue = AnyStruct | AnyEnum
typealias ValueInstance = StructInstance | EnumInstance

// any value which conforms to `SomeProtocol`; reference types finally are out the way
func foo<T>(value: All<AnyValue, SomeProtocol>)  
func foo<T>(value: All<ValueInstance, SomeProtocol>)  

// flattened version
func foo<T>(value: AnyValue & SomeProtocol)  
func foo<T>(value: ValueInstance & SomeProtocol)  
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160516/696ff982/attachment.html>


More information about the swift-evolution mailing list