<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><br class=""><div><br class=""><blockquote type="cite" class=""><div class="">On Dec 2, 2017, at 9:23 PM, Dave Abrahams &lt;<a href="mailto:dabrahams@apple.com" class="">dabrahams@apple.com</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><div style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><br class="Apple-interchange-newline">On Nov 30, 2017, at 2:28 PM, Douglas Gregor via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>&gt; wrote:<br class=""></div><div style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><div class=""><blockquote type="cite" class=""><div class=""><div class=""><b class="">What’s a Good Solution Look Like?</b></div><div class="">Our current system for associated type inference and associated type defaults is buggy and complicated.</div></div></blockquote><div class=""><br class=""></div>Well,<span class="Apple-converted-space">&nbsp;</span><i class="">that’s</i>&nbsp;the problem, then. &nbsp;Don’t worry, I won’t suggest that you simply fix the implementation, because even if there weren’t bugs and the system were predictable I’d still think we could improve the situation for users by making associated type default declarations more explicit.</div><div class=""><br class=""><blockquote type="cite" class=""><div class=""><div class="">The compiler gets it right often enough that people depend on it, but I don’t think anyone can reasonably be expected to puzzle out what’s going to happen, and this area is rife with bugs. If we were to design a new solution from scratch, what properties should it have?</div><div class=""><br class=""></div><div class=""><ul class="MailOutline"><li class="">It should allow the author of a protocol to provide reasonable defaults, so the user doesn’t have to write them</li><li class="">It shouldn’t require users to write typealiases for “obvious” cases, even when they aren’t due to defaults</li><li class="">It shouldn’t infer an inconsistent set of typealiases</li><li class="">It should be something that a competent Swift programmer could reason about when it will succeed, when and why it will fail, and what the resulting inferred typealiases would be</li><li class="">It should admit a reasonable implementation in the compiler that is performant and robust</li></ul></div></div></blockquote>• It should cover all of the existing use cases.</div><div class=""><span style="background-color: rgba(255, 255, 255, 0);" class="">• It should not break code at this point.</span></div><div class="">• We should have a migration strategy for existing code that avoids traps like silent semantic changes.</div><div class=""><br class=""></div><div class="">My bullet is important to me; I don’t think existing use cases are (inherently) so complex that we can sacrifice almost any of them and still end up with a sufficiently useful system. &nbsp;At the very least, existing use cases provide the only guidance we really have as to what the feature should do.</div></div></div></blockquote><div><br class=""></div><div>I honestly don’t feel like a have a good handle on all of the use cases for associated type inference, and it’s not something we can simply search for on GitHub. But I think it covers most of them—and Matthew and Greg’s positive feedback helps my confidence here. The biggest potential issue, I think, is that we’ll no longer infer associated types from default implementations, which protocol vendors might be relying on.</div><br class=""><blockquote type="cite" class=""><div class=""><div style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><div class=""><br class=""></div><div class="">I think we need to acknowledge that my second bullet is unattainable, at least if we want to improve type checking performance. Not breaking any code means that given any existing code, the compiler would have to explore the same solution space it currently does, and come up with the same answers. &nbsp;Improving performance would require new &nbsp;declarations to use totally optional explicit syntax to prevent some explorations, and that’s an untenable user experience.</div></div></div></blockquote><div><br class=""></div>Yes, I agree.</div><div><br class=""><blockquote type="cite" class=""><div class=""><div style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><div class="">Which brings me to my third bullet: unless we are willing to break the code of protocol<span class="Apple-converted-space">&nbsp;</span><i class="">users</i><span class="Apple-converted-space">&nbsp;</span>(as opposed to vendors) we need to ensure that vendors can confidently convert code to use the new system without changing semantics.</div></div></div></blockquote><div><br class=""></div><div>Yeah, (2) below is basically that feature.</div><br class=""><blockquote type="cite" class=""><div class=""><div style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><div class="">&nbsp;</div><div class=""><blockquote type="cite" class=""><div class=""><div class=""><br class=""></div><div class=""><b class="">A Rough Proposal</b></div><div class="">I’ve been thinking about this for a bit, and I think there are three ways in which we should be able to infer an associated type witness:</div><div class=""><br class=""></div><div class=""><ol class="MailOutline"><li class="">Associated type defaults, which are specified with the associated type itself, e.g.,<br class=""><br class=""><font face="Menlo" class="">&nbsp; associatedtype Indices = DefaultIndices&lt;Self&gt;<br class=""></font><br class="">These are easy to reason about for both the programmer and the compiler.</li><li class="">Typealiases in (possibly constrained) protocol extensions, e.g.,<br class=""><br class=""><font face="Menlo" class="">&nbsp; extension RandomAccessCollection where Index : Strideable, Index.Stride == IndexDistance {</font><br class=""><font face="Menlo" class="">&nbsp; &nbsp; typealias RandomAccessCollection.Indices = CountableRange&lt;Index&gt;</font><br class=""><font face="Menlo" class="">&nbsp; }</font><br class=""><br class="">I’m intentionally using some odd ‘.’ syntax here to indicate that this typealias is intended only to be found when trying to satisfy an associated type requirement, and is not a general typealias that could be found by normal name lookup. Let’s set the syntax bike shed aside for the moment. The primary advantage of this approach (vs. inferring Indices from “var Indices: CountableRange&lt;Index&gt;” in a constrained protocol extension) is that there’s a real typealias declaration that compiler and programmer alike can look at and reason about based just on the name “Indices”.<span class="Apple-converted-space">&nbsp;</span><br class=""><br class="">Note that this mechanism technically obviates the need for (1), in the same sense that&nbsp;<a href="https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#default-implementations-in-protocols-" class="">default implementations in protocols</a>&nbsp;are merely syntactic sugar.</li><li class="">Declarations within the nominal type declaration or extension that declares conformance to the protocol in question. This is generally the same approach as described in “associated type inference” above, where we match requirements of the protocol against declarations that could satisfy those requirements and infer associated types from there. However, I want to turn it around: instead of starting with the requirements of the protocol any looking basically anywhere in the type or any protocol to which it conforms (for implementations in protocol extensions), start with the declarations that the user explicitly wrote at the point of the conformance and look for requirements they might satisfy. For example, consider our initial example:<br class=""><br class=""><div class=""><font face="Menlo" class="">&nbsp; extension MyCollection: RandomAccessCollection { &nbsp; &nbsp;</font></div><div class=""><font face="Menlo" class="">&nbsp; &nbsp; var startIndex: Int { return contents.startIndex }</font></div><div class=""><font face="Menlo" class="">&nbsp; &nbsp; var endIndex: Int { return contents.endIndex }</font></div><div class=""><font face="Menlo" class="">&nbsp; &nbsp; subscript(index: Int) -&gt; T { return contents[index] }</font></div><div class=""><font face="Menlo" class="">&nbsp; }<br class=""><br class=""></font></div>Since startIndex, endIndex, and subscript(_:) are declared in the same extension that declares conformance to RandomAccessIterator, we should look for requirements with the same name as these properties and subscript within RandomAccessCollection (or any protocol it inherits) and infer Index := Int and Element := T by matching the type signatures. This is still the most magical inference rule, because there is no declaration named “Index” or “Element” to look at. However, it is much narrower in scope than the current implementation, because it’s only going to reason from the (probably small) set of declarations that the user wrote alongside the conformance, so it’s more likely to be intentional. Note that this is again nudging programmers toward the style of programming where one puts one protocol conformance per extension, which is admittedly my personal preference.</li></ol></div><div class=""><div class=""><br class=""></div></div><div class=""><b class="">Thoughts?</b></div></div></blockquote><div class=""><br class=""></div>The thing that strikes me most about these is that the first two are explicit declarations of intent: “In the absences of an explicit declaration, deduce this associated type as follows,” while the third is still extremely indirect. &nbsp;While it hints to the compiler about which conformances’ associated type requirements we are trying to satisfy, it never comes out and says straight out what the associated type should be, even though it needs to be mentioned. &nbsp;As a generic programmer, I don’t value the concision gained over the clarity lost, and I’d like to see the solutions to these problems follow the explicit-declaration-of-intent pattern. &nbsp;However, the code in #3 is not written by the protocol vendor, and for me it is (at least currently) a stretch to think of breaking the code of protocol users, so I grudgingly accept it. &nbsp;</div></div></div></blockquote><div><br class=""></div>Sums up my feelings about #3 pretty well.</div><div><br class=""><blockquote type="cite" class=""><div class=""><div style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><div class=""><br class=""></div><div class="">If we were<span class="Apple-converted-space">&nbsp;</span><i class="">really</i>&nbsp;starting from scratch I might suggest requiring that conformances use the associated type name rather than some concrete type, e.g.&nbsp;</div><div class=""><br class=""></div><div class=""><div class=""><font class="" style="background-color: rgba(255, 255, 255, 0);">extension MyCollection: RandomAccessCollection { &nbsp; &nbsp;</font></div><div class=""><font class="" style="background-color: rgba(255, 255, 255, 0);">&nbsp; &nbsp; typealias RandomAccessCollection.Index = Int</font></div><div class=""><font class="" style="background-color: rgba(255, 255, 255, 0);">&nbsp; &nbsp; var startIndex: Index { return contents.startIndex }</font></div><div class=""><font class="" style="background-color: rgba(255, 255, 255, 0);">&nbsp; &nbsp; var endIndex: Index { return contents.endIndex }</font></div><div class=""><font class="" style="background-color: rgba(255, 255, 255, 0);">&nbsp; &nbsp; subscript(index: Index) -&gt; Element { return contents[index] }</font></div><div class=""><font class="" style="background-color: rgba(255, 255, 255, 0);">&nbsp; }</font></div></div><div class=""><font class="" style="background-color: rgba(255, 255, 255, 0);"><br class=""></font></div><div class=""><font class="" style="background-color: rgba(255, 255, 255, 0);">But I suspect we’re well past the point in the language’s evolution where that sort of change is possible.</font></div></div></div></blockquote><div><br class=""></div><div>I’d like to *allow* that, for all declarations that are meant to conform to a protocol, but we can’t (and IMO shouldn’t) require it.</div><br class=""><blockquote type="cite" class=""><div class=""><div style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><div class="">As for migration of protocol user code, I think we’d need to run both the new and the old slow inference in the migrator and flag any differences. &nbsp;I don’t know what to do about protocol vendors’ code though.</div></div></div></blockquote><div><br class=""></div><div>Yeah. We might simply need to run the old inference in Swift 4 mode when the new inference doesn’t succeed, and warn + Fix-It the missing typealiases when the old succeeds but the new fails.</div><br class=""><blockquote type="cite" class=""><div class=""><div style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><div class=""><blockquote type="cite" class=""><div class=""><div class="">I think this approach is more predictable and more implementable than the current model. I’m curious whether the above makes sense to someone other than me, and whether it covers existing use cases well enough. Thoughts?</div></div></blockquote><br class=""></div><div class="">Well, covering the use cases is definitely still a concern for me. &nbsp;I don’t think we’ll know for sure until we try it, but have you thought about how to migrate each piece of code in the standard library? &nbsp;Does it cover those cases?</div></div></div></blockquote><br class=""></div><div>I’ve looked at the various defaulted associated types in the Sequence/Collection hierarchy, and I think they’ll work better with this scheme than they do currently. Honestly, I think I have to go implement it to see how things work out.</div><div><br class=""></div><div><span class="Apple-tab-span" style="white-space:pre">        </span>- Doug</div><div><br class=""></div><br class=""></body></html>