[swift-evolution] [RFC] Associated type inference

Nevin Brackett-Rozinsky nevin.brackettrozinsky at gmail.com
Fri Dec 1 13:42:23 CST 2017


On Thu, Nov 30, 2017 at 7:28 PM, Douglas Gregor via swift-evolution <
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:
>
>
>    1. 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.
>    2. 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.
>    3. 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?

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”.

Nevin
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20171201/9ca4e08d/attachment.html>


More information about the swift-evolution mailing list