<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body dir="auto"><div><br></div><div><br>On Jun 9, 2016, at 12:49 AM, Austin Zheng via swift-evolution <<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>> wrote:<br><br></div><blockquote type="cite"><div><div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Jun 8, 2016 at 3:22 PM, Dave Abrahams <span dir="ltr"><<a href="mailto:dabrahams@apple.com" target="_blank">dabrahams@apple.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><span class=""><br>
on Wed Jun 08 2016, Austin Zheng <<a href="http://austinzheng-at-gmail.com">austinzheng-AT-gmail.com</a>> wrote:<br>
<br>
> FWIW my opinion is that existentials either shouldn't be allowed to stand<br>
> in for generic type parameters, or Dave's option #1 if they are.<br>
<br>
</span>Don't you mean #2? Otherwise I'm confused. #1 is the one that<br>
prohibits more usages.<br></blockquote><div><br></div><div>I'm just being cautious until a better solution comes along.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">
<span class=""><br>
> The implied promise of a generic type parameter T right now is that T<br>
> always stands for the same concrete type (modulo things like passing<br>
> in a subclass where a class would do), and likewise for all of T's<br>
> associated types (T.Foo is always the same type everywhere in the<br>
> context where T is valid). This is what makes using anything with<br>
> 'self' requirements in a generic context sound. Allowing existentials<br>
> to satisfy T would violate that constraint.<br>
<br>
</span>Not if you consider Any<Collection where Element == Int> to be a<br>
concrete type. Concrete w.r.t. to a generic parameter means something<br>
different from Concrete w.r.t. subtyping.<br></blockquote><div><br></div><div>You can consider Any<Collection where .Element == Int> to be a concrete type, but then you have the unprecedented situation where the associated types associated with a concrete type aren't necessarily the same for all instances (is this true for any type that can satisfy a generic type parameter today?).</div><div><br></div><div>(For the sake of this argument, Array isn't a concrete type, but Array<T> or Array<Int> is. You can't use 'Array' anywhere in Swift today, so I think my assertion is fair.)</div><div><br></div><div>My understanding is that fixing the generic type parameter T by specializing a function/type also fixes all the associated types associated with T. This 'contract' would have to be weakened to allow existential types to satisfy generic type parameters in any non-trivial way.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">
<span class=""><br>
> Relaxing these semantics would make it too easy to write code that<br>
> traps at runtime "without the user having to reach" (to paraphrase<br>
> Jordan from the "Swift philosophy" thread). Anyone who really really<br>
> wants to write code that is 'compile-time unsound' in this way should<br>
> have to explicitly type erase using concrete wrappers.<br>
<br>
</span>I'd really like to see the use-cases, to make sure that these restricted<br>
existentials will be useful enough to be worth implementing.<br></blockquote><div><br></div><div>This is enormously important.</div><div><br></div><div>First of all, (of course you know) there is a semantic difference between:</div><div><br></div><div>protocol Pet : class { }; class Cat : Pet { }; class Dog : Pet { }</div><div><br></div><div>class AnimalShelter<T : Pet> { var pet: T }</div><div><br></div><div>and </div><div><br></div><div>class AnimalShelter { var pet: Pet }</div><div><br></div><div>This is something you can express for simple existentials today (the code above should run, with minor modifications), but not for existentials containing associated types or self requirements.</div><div><br></div><div>I think a big part of what people want to do is to declare variables, pass args to functions, and get return values from functions that abstract over something like Collection. They want to do this without having to make their code generic, and without forcing their code to be homogenous at runtime (e.g. an instance of the dynamic AnimalShelter can be populated with a cat and later a dog, but the generic one can only ever contain either; extend this to Collections of Ints or whatnot).</div><div><br></div><div>The big problem is that existentials can't guarantee that they satisfy the contract generic functions and types are expecting, as we've been discussing.</div><div><br></div><div>To be honest, I think requiring existentials to be opened should be a prerequisite to using them generically. This would define away the impedance mismatch at the expense of making certain things marginally harder to do. (An alternate way of thinking about it is that it would make the potential for a runtime error explicit, and localize it):</div><div><br></div><div>func doSomething<T : Collection where T.Element == Int>(x: C, y: C) { ... }</div><div><br></div><div>let a : Any<Collection where .Element == Int></div><div>let b : Any<Collection where .Element == Int></div><div><br></div><div>// Prohibit this...</div><div>// doSomething(a, b)</div><div><br></div><div>// Allow this:</div><div>if let a = a openas? T, b = b as? T {</div><div> // We've recovered the 'strong guarantee' that doSomething expects for T</div><div> doSomething(a, b)</div><div>} else {</div><div> // Here's our trap</div><div>}</div></div></div></div></div></blockquote><div><br></div><div>Right... exactly what I wrote yesterday, from Doug having written it months ago.</div><div><br></div><div>You are still missing the dynamicType checking to make invoking comparable{} methods work.</div><div><br></div><div>and that could further be simplified with something like</div><div><br></div><div>If letAs a:T , y:T {</div><div> // compatible types, but still possible for dynamic types to be different</div><div>}</div><div><br></div><div>LetAs is a strawman for </div><div> Let a = a openas? T</div><br><blockquote type="cite"><div><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div><br></div><div>The biggest problem is that this sort of solution would prohibit existentials from being used in generic contexts where the existentials only have to be "similar enough", not identical, for things to work out. Given how fuzzy "similar enough" has proven to be, maybe that's not a terrible tradeoff.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex">
<div class=""><div class="h5"><br>
><br>
><br>
> Best,<br>
> Austin<br>
><br>
> On Wed, Jun 8, 2016 at 2:37 PM, Austin Zheng <<a href="mailto:austinzheng@gmail.com">austinzheng@gmail.com</a>> wrote:<br>
><br>
>> We might be talking past each other. I think Matthew is talking about<br>
>> using an existential outside the context of generic functions. For example,<br>
>> something like this should be trap-proof (as long as 'x' is immutable,<br>
>> which it is in this example):<br>
>><br>
>> func copySequenceIntoArray(x: Any<Sequence where .Iterator.Element ==<br>
>> Int>) -> [Int] {<br>
>> var buffer : [Int] = []<br>
>> // Stupid implementation to make a point<br>
>> var iterator : x.Iterator = x.makeIterator()<br>
>> while true {<br>
>> let nextItem : Int? = iterator.next()<br>
>> if let nextItem = nextItem {<br>
>> buffer.append(nextItem)<br>
>> } else {<br>
>> return buffer<br>
>> }<br>
>> }<br>
>> }<br>
>><br>
>> Even this would never trap as well:<br>
>><br>
>> func copySequenceIntoArray<T>(x: Any<Sequence where .Iterator.Element ==<br>
>> T>) -> [T] {<br>
>> var buffer : [T] = []<br>
>> for item in x {<br>
>> buffer.append(item)<br>
>> }<br>
>> return buffer<br>
>> }<br>
>><br>
>> Where we run into difficulty is something like this (forgive my abuse of<br>
>> the collections API; I don't remember all the new indexing APIs off the top<br>
>> of my head):<br>
>><br>
>> func doSomething<T : Collection where T.Element == Int>(x: T, y: T) {<br>
>> // Get indexes out of x and use them to index into y<br>
>> var idx = x.startIndex<br>
>> while (idx != x.endIndex || idx != y.endIndex) {<br>
>> print(x[idx])<br>
>> print(y[idx])<br>
>> idx = x.nextIndex(idx)<br>
>> }<br>
>> }<br>
>> let someSeq : Any<Collection where .Element == Int> = // ...<br>
>> let anotherSeq : Any<Collection where .Element == Int> = // ...<br>
>> // Trouble!<br>
>> // someSeq and anotherSeq are the same existential type<br>
>> // But the concrete index types within each of the existential variables<br>
>> may be different<br>
>> doSomething(someSeq, anotherSeq)<br>
>><br>
>> It's this situation (using an existential type to fulfill a generic type<br>
>> parameter constrained to the same requirements that comprise that<br>
>> existential) that requires either of the two options that Dave presented,<br>
>> due to our lack of compile-time type information about the fulfilling<br>
>> type's associated types.<br>
>><br>
>> Best,<br>
>> Austin<br>
>><br>
>> On Wed, Jun 8, 2016 at 2:33 PM, Matthew Johnson via swift-evolution <<br>
>> <a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>> wrote:<br>
>><br>
>>><br>
>>><br>
>>> Sent from my iPad<br>
>>><br>
>>> > On Jun 8, 2016, at 3:16 PM, Dave Abrahams via swift-evolution <<br>
>>> <a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>> wrote:<br>
>>> ><br>
>>> ><br>
>>> >> on Wed Jun 08 2016, Thorsten Seitz <<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>> wrote:<br>
>>> >><br>
>>> >> Ah, thanks, I forgot! I still consider this a bug, though (will have<br>
>>> >> to read up again what the reasons are for that behavior).<br>
>>> ><br>
>>> > Yes, but in the case of the issue we're discussing, the choices are:<br>
>>> ><br>
>>> > 1. Omit from the existential's API any protocol requirements that depend<br>
>>> > on Self or associated types, in which case it *can't* conform to<br>
>>> > itself because it doesn't fulfill the requirements.<br>
>>><br>
>>> They don't need to be omitted. They are exposed in different ways<br>
>>> depending on how the existential is constrained. Austin's proposal was<br>
>>> originally written to omit some members but it was modified based on<br>
>>> feedback from Doug Gregor IIRC (Austin, is that right?). Now it contains<br>
>>> examples showing how these members are made available in a safe way. Some<br>
>>> members may still not be usable because you can't form an argument but IIRC<br>
>>> the suggestion was that they be exposed anyway for consistency.<br>
>>><br>
>>> ><br>
>>> > 2. Erase type relationships and trap at runtime when they don't line up.<br>
>>> ><br>
>>> > Matthew has been arguing against #2, but you can't “fix the bug” without<br>
>>> > it.<br>
>>> ><br>
>>> >><br>
>>> >> -Thorsten<br>
>>> >><br>
>>> >>> Am 08.06.2016 um 21:43 schrieb Austin Zheng <<a href="mailto:austinzheng@gmail.com">austinzheng@gmail.com</a>>:<br>
>>> >>><br>
>>> >>> It's not possible, even with Swift's current implementation of<br>
>>> >>> existentials. A protocol type P isn't considered to conform to<br>
>>> >>> itself, thus the following is rejected:<br>
>>> >>><br>
>>> >>> let a : MyProtocol = // ...<br>
>>> >>> func myFunc<T : MyProtocol>(x: T) {<br>
>>> >>> // ....<br>
>>> >>> }<br>
>>> >>> myFunc(a) // "Cannot invoke 'myFunc' with an argument list of type<br>
>>> MyProtocol"<br>
>>> >>><br>
>>> >>> Changing how this works is probably worth a proposal by itself.<br>
>>> >>><br>
>>> >>> Austin<br>
>>> >>><br>
>>> >>><br>
>>> >>> On Wed, Jun 8, 2016 at 12:34 PM, Thorsten Seitz via swift-evolution<br>
>>> >>> <<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a><br>
>>> >>> <mailto:<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>>><br>
>>> >>> wrote:<br>
>>> >>><br>
>>> >>>> Am 08.06.2016 um 20:33 schrieb Dave Abrahams via swift-evolution<br>
>>> >>>> <<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a><br>
>>> >>>> <mailto:<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>>>:<br>
>>> >>>><br>
>>> >>>><br>
>>> >>>> on Tue Jun 07 2016, Matthew Johnson <<a href="http://matthew-at-anandabits.com">matthew-AT-anandabits.com</a>><br>
>>> wrote:<br>
>>> >>>><br>
>>> >>>>>> On Jun 7, 2016, at 9:15 PM, Dave Abrahams<br>
>>> >>>>>> <<a href="mailto:dabrahams@apple.com">dabrahams@apple.com</a><br>
>>> >>>>>> <mailto:<a href="mailto:dabrahams@apple.com">dabrahams@apple.com</a>>><br>
>>> >>>>>> wrote:<br>
>>> >>>>>><br>
>>> >>>>>><br>
>>> >>>>>> on Tue Jun 07 2016, Matthew Johnson <<a href="http://matthew-at-anandabits.com">matthew-AT-anandabits.com</a><br>
>>> >>>>>> <<a href="http://matthew-at-anandabits.com/" rel="noreferrer" target="_blank">http://matthew-at-anandabits.com/</a><br>
>>> >>>>>> <<a href="http://matthew-at-anandabits.com/" rel="noreferrer" target="_blank">http://matthew-at-anandabits.com/</a>>>> wrote:<br>
>>> >>>>><br>
>>> >>>>>>>> On Jun 7, 2016, at 4:13 PM, Dave Abrahams via swift-evolution<br>
>>> >>>>>>>> <<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a><br>
>>> >>>>>>>> <mailto:<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>>><br>
>>> >>>>>>>> wrote:<br>
>>> >>>>>>>><br>
>>> >>>>>>>><br>
>>> >>>>>>>> on Tue Jun 07 2016, Matthew Johnson<br>
>>> >>>>>>>> <<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a><br>
>>> >>>>>>>> <mailto:<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>>><br>
>>> >>>>>>>> wrote:<br>
>>> >>>>>>><br>
>>> >>>>>>>>>> , but haven't realized<br>
>>> >>>>>>>>>> that if you step around the type relationships encoded in Self<br>
>>> >>>>>>>>>> requirements and associated types you end up with types that<br>
>>> appear to<br>
>>> >>>>>>>>>> interoperate but in fact trap at runtime unless used in<br>
>>> exactly the<br>
>>> >>>>>>>>>> right way.<br>
>>> >>>>>>>>><br>
>>> >>>>>>>>> Trap at runtime? How so? Generalized existentials should<br>
>>> still be<br>
>>> >>>>>>>>> type-safe.<br>
>>> >>>>>>>><br>
>>> >>>>>>>> There are two choices when you erase static type relationships:<br>
>>> >>>>>>>><br>
>>> >>>>>>>> 1. Acheive type-safety by trapping at runtime<br>
>>> >>>>>>>><br>
>>> >>>>>>>> FloatingPoint(3.0 as Float) + FloatingPoint(3.0 as Double) //<br>
>>> trap<br>
>>> >>>>>>>><br>
>>> >>>>>>>> 2. Don't expose protocol requirements that involve these<br>
>>> relationships,<br>
>>> >>>>>>>> which would prevent the code above from compiling and prevent<br>
>>> >>>>>>>> FloatingPoint from conforming to itself.<br>
>>> >>>>>>>><br>
>>> >>>>>>>>> Or are you talking about the hypothetical types / behaviors<br>
>>> people<br>
>>> >>>>>>>>> think they want when they don’t fully understand what is<br>
>>> happening...<br>
>>> >>>>>>>><br>
>>> >>>>>>>> I don't know what you mean here. I think generalized<br>
>>> existentials will<br>
>>> >>>>>>>> be nice to have, but I think most people will want them to do<br>
>>> something<br>
>>> >>>>>>>> they can't possibly do.<br>
>>> >>>>>>><br>
>>> >>>>>>> Exactly. What I meant is that people think they want that<br>
>>> expression<br>
>>> >>>>>>> to compile because they don’t understand that the only thing it<br>
>>> can do<br>
>>> >>>>>>> is trap. I said “hypothetical” because producing a compile time<br>
>>> error<br>
>>> >>>>>>> rather than a runtime trap is the only sane thing to do. Your<br>
>>> comment<br>
>>> >>>>>>> surprised me because I can’t imagine we would move forward in<br>
>>> Swift<br>
>>> >>>>>>> with the approach of trapping.<br>
>>> >>>>>><br>
>>> >>>>>> I would very much like to be able to create instances of<br>
>>> “Collection<br>
>>> >>>>>> where Element == Int” so we can throw away the wrappers in the<br>
>>> stdlib.<br>
>>> >>>>>> That will require some type mismatches to be caught at runtime via<br>
>>> >>>>>> trapping.<br>
>>> >>>>><br>
>>> >>>>> For invalid index because the existential accepts a type erased<br>
>>> index?<br>
>>> >>>><br>
>>> >>>> Exactly.<br>
>>> >>>><br>
>>> >>>>> How do you decide where to draw the line here? It feels like a very<br>
>>> >>>>> slippery slope for a language where safety is a stated priority to<br>
>>> >>>>> start adopting a strategy of runtime trapping for something as<br>
>>> >>>>> fundamental as how you expose members on an existential.<br>
>>> >>>><br>
>>> >>>> If you don't do this, the alternative is that “Collection where<br>
>>> Element<br>
>>> >>>> == Int” does not conform to Collection. That's weird and not very<br>
>>> >>>> useful. You could expose all the methods that were on protocol<br>
>>> >>>> extensions of Collection on this existential, unless they used<br>
>>> >>>> associated types other than the element type. But you couldn't pass<br>
>>> the<br>
>>> >>>> existential to a generic function like<br>
>>> >>>><br>
>>> >>>> func scrambled<C: Collection>(_ c: C) -> [C.Element]<br>
>>> >>><br>
>>> >>> I don’t understand. Why couldn’t an existential be passed to that<br>
>>> function?<br>
>>> >>><br>
>>> >>> -Thorsten<br>
>>> >>><br>
>>> >>><br>
>>> >>><br>
>>> >>>><br>
>>> >>>>> IMO you should *have* to introduce unsafe behavior like that<br>
>>> manually.<br>
>>> >>>><br>
>>> >>>> Collection where Element == Int & Index == *<br>
>>> >>>><br>
>>> >>>> ?<br>
>>> >>>><br>
>>> >>>>> Collection indices are already something that isn’t fully statically<br>
>>> >>>>> safe so I understand why you might want to allow this.<br>
>>> >>>><br>
>>> >>>> By the same measure, so are Ints :-)<br>
>>> >>>><br>
>>> >>>> The fact that a type's methods have preconditions does *not* make it<br>
>>> >>>> “statically unsafe.”<br>
>>> >>>><br>
>>> >>>>> But I don’t think having the language's existentials do this<br>
>>> >>>>> automatically is the right approach. Maybe there is another<br>
>>> approach<br>
>>> >>>>> that could be used in targeted use cases where the less safe<br>
>>> behavior<br>
>>> >>>>> makes sense and is carefully designed.<br>
>>> >>>><br>
>>> >>>> Whether it makes sense or not really depends on the use-cases.<br>
>>> There's<br>
>>> >>>> little point in generalizing existentials if the result isn't very<br>
>>> useful.<br>
>>> >>>> The way to find out is to take a look at the examples we currently<br>
>>> have<br>
>>> >>>> of protocols with associated types or Self requirements and consider<br>
>>> >>>> what you'd be able to do with their existentials if type<br>
>>> relationships<br>
>>> >>>> couldn't be erased.<br>
>>> >>>><br>
>>> >>>> We have known use-cases, currently emulated in the standard library,<br>
>>> for<br>
>>> >>>> existentials with erased type relationships. *If* these represent<br>
>>> the<br>
>>> >>>> predominant use cases for something like generalized existentials, it<br>
>>> >>>> seems to me that the language feature should support that. Note: I<br>
>>> have<br>
>>> >>>> not seen anyone build an emulation of the other kind of generalized<br>
>>> >>>> existential. My theory: there's a good reason for that :-).<br>
>>> >>>><br>
>>> >>>> --<br>
>>> >>>> Dave<br>
>>> >>>> _______________________________________________<br>
>>> >>>> swift-evolution mailing list<br>
>>> >>>> <a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a><br>
>>> >>>> <mailto:<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>><br>
>>> >>>> <a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br>
>>> >>>> <<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a>><br>
>>> >>><br>
>>> >>> _______________________________________________<br>
>>> >>> swift-evolution mailing list<br>
>>> >>> <a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a><br>
>>> >>> <mailto:<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>><br>
>>> >>> <a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br>
>>> >>> <<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a>><br>
>>> >><br>
>>> >> _______________________________________________<br>
>>> >> swift-evolution mailing list<br>
>>> >> <a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a><br>
>>> >> <a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br>
>>> ><br>
>>> > --<br>
>>> > Dave<br>
>>> ><br>
>>> > _______________________________________________<br>
>>> > swift-evolution mailing list<br>
>>> > <a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a><br>
>>> > <a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br>
>>><br>
>>> _______________________________________________<br>
>>> swift-evolution mailing list<br>
>>> <a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a><br>
>>> <a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailman/listinfo/swift-evolution</a><br>
>>><br>
>><br>
>><br>
<br>
</div></div><span class=""><font color="#888888">--<br>
Dave<br>
</font></span></blockquote></div><br></div></div>
</div></blockquote><blockquote type="cite"><div><span>_______________________________________________</span><br><span>swift-evolution mailing list</span><br><span><a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a></span><br><span><a href="https://lists.swift.org/mailman/listinfo/swift-evolution">https://lists.swift.org/mailman/listinfo/swift-evolution</a></span><br></div></blockquote></body></html>