[swift-evolution] Enhanced existential types proposal discussion

Adrian Zubarev adrian.zubarev at devandartist.com
Wed May 18 16:42:07 CDT 2016

Please excuse me for my horrible writing. I guess I’m to tired right now and write a ton strange sentences and typos. :/

Anyways, when the right time comes, I’ll fully support this feature. Any is defiantly worth being part of Swift.

Good n8 everyone.

Adrian Zubarev
Sent with Airmail

Am 18. Mai 2016 bei 23:30:57, Adrian Zubarev (adrian.zubarev at devandartist.com) schrieb:

Okay I got it now, generics made my head spin. I also received no feedback on my proposal to that specific case, which I misunderstood. Thank you for clarifying that to me. That said it makes now sense to force the first requirement, which I dropped in my proposal. 

I’ll fix that tomorrow.

I’m fine with your proposal now. =) 

Adrian Zubarev
Sent with Airmail

Am 18. Mai 2016 bei 23:17:47, Austin Zheng (austinzheng at gmail.com) schrieb:

I'm not sure what your objections actually are, but my responses are inline.

On Wed, May 18, 2016 at 1:57 PM, Adrian Zubarev via swift-evolution <swift-evolution at swift.org> wrote:
So without any initial constraints how would one use this generic function??

extension UIButton: ProtocolA {}

let button = UIButton()
let shadowedButton: ProtocolA = UIButton()

// creates a set of a least one element if the generic type could be inferred
func unionIfPossible<T, U>(_ a: T, _ b: U) -> Set<Any<T, U>>? { /* merge somehow if possible */ }

You would not be able to form Any<T, U> here because T and U are generic arguments unknown.  We don’t accept those, only `class`, specific classes, and specific protocols (and recursively, other Any).

Why would Any<…> does not work with the generic system here? To me it makes no sense, I as a developer would assume I can use a generic Type anywhere a type can be used (protocols and their associated types are a special case).

Because what existentials are trying to model and what generic type parameters are trying to model are two different things. You already cannot use arbitrary types within a Any<> existential as the proposal stands.

This doesn't currently work in Swift, and it shouldn't: "func foo<A, B>() -> protocol<A, B>". There is no useful way to generically compose instances of two protocol types at runtime to form an instance of a type that conforms to both protocols, like there is a way to compose instances of two types to form a tuple containing those types. Therefore, writing a generic function to do this makes no sense.

I would assume that:

func foo<T: UIView>(value: Any<T, SomeProtocol>)

should be equal to (without the need of generics):

func foo(value: Any<UIView, SomeProtocol>)

// this should be valid because the compiler will assume Any<UIView, ProtocolA> where T == UIView and U == ProtocolA
let merged_1: Set<Any<UIView, ProtocolA>> = unionIfPossible( /* UIView subtype */ button, /* ProtocolA */ shadowedButton) 

// this won’t be possible because of the restriction
let merged_2: Set<Any<UIView, ProtocolA>> = unionIfPossible(shadowedButton, button) 

Any<UIView, ProtocolA> != Any<ProtocolA, UIView> isn’t right. Sure it may feel right for readability but the types should be equal.

"Can be any class type that is a UIView or a subclass of UIView, that also conforms to ProtocolA.“ == "Type that conforms to ProtocolA and that is a UIView or a subclass of UIView.“

This is also a nesting problem where you will be forced to choose the right place inside the angle brackets where to add a nested `Any<…>`.

class A: ClassB, ProtocolA {}

Any<A, Any<ClassB, ProtocolA>> == A != Any<Any<ClassB, ProtocolA>, A> == Any<ClassB, ProtocolA, A> which should be reorder by the compiler and inferred as A

`Any<Any<ClassB, ProtocolA>, A>` is not allowed because if a class is provided it must come first.

`Any<ClassB, ProtocolA, A>` is not allowed because it contains more than one class.
Not true, take a look at the nested section of the proposal again.

We discussed that this is valid:

// Allowed, but pointless.
// Identical to Any<ProtocolA, ProtocolB>
let b : Any<Any<ProtocolA, ProtocolB>>

This implies that also this would be valid:

Any<Any<ClassA, ProtocolA>> inferred as Any<ClassA, ProtocolA>

Yes, this is valid.

There is another example:

// Can be any type that is a UITableView conforming to ProtocolA.
// UITableView is the most specific class, and it is a subclass of the other
// two classes.
let a : Any<UIScrollView, Any<UITableView, Any<UIView, ProtocolA>>>

Which followed by the mentioned rule can als be:

Any<UIScrollView, UITableView, Any<UIView, ProtocolA>> or
Any<UIScrollView, UITableView, UIView, ProtocolA>

This would force us to allow multiple classes inside Any<…> if there is a inheritance relationship between them.

Yes. As explained in the proposal, there is already a way to handle resolving the single 'effective' class constraint. You can only 'explicitly' declare one class constraint, but you can pull in other class constraints from nested existentials for the sake of making composition easier. In the end it doesn't matter, because if the class constraints don't form a valid inheritance hierarchy the compiler will complain, and if they do the most specific type will be chosen.

And because this is a inheritance relationship it can be simplified to:

Any<UITableView, ProtocolA> which is valid. 

And by the way since the order in your proposal would matter, this example won’t work at all, because its not:

Any<UITableView, Any<UIScrollView, Any<UIView, ProtocolA>>> 

The order does not affect the semantic meaning of the existential. There is a section in the proposal on how existentials are conceptually 'reduced' from whatever form they take when the programmer types them in, please read it. I am not proposing a macro system. The compiler does not do textual replacement in order to flatten nested existential definitions.

This is also why it makes no sense to have a generic "Any<T, U>", because such a type is identical to "Any<U, T>", which is not true for any other generic type or aggregate type in Swift.

In my proposal I explicitly banned multiple reference types and inheritance branches like this. I would need to simplify that type to Any<UITableView, ProtocolA> by myself.
Ingeneral this is a good idea, but it makes nesting (with typealiases) almost impossible, when there are some relationship like in the above example.

Adrian Zubarev
Sent with Airmail

swift-evolution mailing list
swift-evolution at swift.org

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160518/1cc5436e/attachment.html>

More information about the swift-evolution mailing list