[swift-evolution] [RFC] Associated type inference

Douglas Gregor dgregor at apple.com
Thu Dec 7 17:05:04 CST 2017



> On Dec 1, 2017, at 11:42 AM, Nevin Brackett-Rozinsky <nevin.brackettrozinsky at gmail.com> wrote:
> 
> On Thu, Nov 30, 2017 at 7:28 PM, Douglas Gregor via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
> A Rough Proposal
> 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:
> 
> Associated type defaults, which are specified with the associated type itself, e.g.,
> 
>   associatedtype Indices = DefaultIndices<Self>
> 
> These are easy to reason about for both the programmer and the compiler.
> Typealiases in (possibly constrained) protocol extensions, e.g.,
> 
>   extension RandomAccessCollection where Index : Strideable, Index.Stride == IndexDistance {
>     typealias RandomAccessCollection.Indices = CountableRange<Index>
>   }
> 
> 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<Index>” 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”. 
> 
> Note that this mechanism technically obviates the need for (1), in the same sense that default implementations in protocols <https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#default-implementations-in-protocols-> are merely syntactic sugar.
> 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:
> 
>   extension MyCollection: RandomAccessCollection {    
>     var startIndex: Int { return contents.startIndex }
>     var endIndex: Int { return contents.endIndex }
>     subscript(index: Int) -> T { return contents[index] }
>   }
> 
> 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.
> 
> Thoughts?
> 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?
> 
> 	- Doug
> 
> 
> How does this work with retroactive conformance, especially where all the protocol requirements already exist on a type and an empty extension declares conformance? For example, suppose Module A declares a protocol with associated types, and Module B has a struct which naturally possesses all the required members to conform (maybe B handles Double concretely, while A can work with any FloatingPoint, or some such). As a client importing both modules and providing an empty extension to conform B’s struct to A’s protocol, will the associated types be inferred?

No, the associated types will not be inferred in this case. That will be a change in behavior (and a source compatibility regression).

> Also, have you considered the possibility of allowing protocol authors to specify which types should be inferred from which requirements? For example Collection might demarcate “startIndex” as the source-of-truth for inferring “Index”, and “subscript (Index)->Element” as the source-of-truth for inferring “Element”.

This did come up a while ago, but it always felt like a hack… why infer from startIndex/endIndex rather than from “indices”, given that one set can be implemented in terms of the other with sufficiently-good inference? Why should the protocol author have to make sure a decision?

	- Doug


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20171207/3d136b51/attachment.html>


More information about the swift-evolution mailing list