[swift-evolution] [Review] SE-0143: Conditional Conformances

Douglas Gregor dgregor at apple.com
Wed Oct 5 17:51:17 CDT 2016


> On Sep 30, 2016, at 5:59 PM, Dave Abrahams <dabrahams at apple.com> wrote:
> 
> 
> on Fri Sep 30 2016, Douglas Gregor <dgregor-AT-apple.com <http://dgregor-at-apple.com/>> wrote:
> 
>>> On Sep 30, 2016, at 1:41 PM, Dave Abrahams <dabrahams at apple.com> wrote:
>>> 
>>> 
>>> on Fri Sep 30 2016, Douglas Gregor <dgregor-AT-apple.com> wrote:
>>> 
>>>>>> However, in cases where there is a reasonable ordering between the two
>>>>>> constrained extensions (i.e., one is more specialized than the other),
>>>>>> the less specialized constrained extension should "win" the implied
>>>>>> conformance. 
>>>>> 
>>>>> “Less specialized wins” is surprising at the very least!  I've only ever
>>>>> seen the opposite rule (e.g. in overload resolution).  What's the
>>>>> rationale for this approach?
>>>> 
>>>> You need the most-general implementation so that conformance can be
>>>> used by other constrained extensions, the way the ‘Array: Equatable’
>>>> gets used by ‘Array: Hashable’ and ‘Array: Comparable’.
>>> 
>>> Are you sure we don't want the most specialized common ancestor of all
>>> the constrained extensions?
>> 
>> Yes, that’s correct.
> 
> We do or don't want that?

We want the most specialized common ancestor, i.e., the best implementation that can be used by all of the extensions that imply a conformance to Equatable.

> 
>>>>>> Continuing the example from above:
>>>>>> 
>>>>>> ```swift
>>>>>> protocol S: R { }
>>>>>> 
>>>>>> struct X5<T> { }
>>>>>> 
>>>>>> extension X5: S where T: S { }
>>>>>> 
>>>>>> // This last extension "wins" the implied conformance to P, because
>>>>>> // the extension where "T: R" is less specialized than the one
>>>>>> // where "T: S".
>>>>>> extension X5: R where T: R { }
>>>>>> ```
>>>>>> 
>>>>>> Thus, the rule for placing implied conformances is to pick the *least
>>>>>> specialized* extension that implies the conformance. If there is more
>>>>>> than one such extension, then either:
>>>>>> 
>>>>>> 1. All such extensions are not constrained extensions (i.e., they have
>>>>>> no requirements beyond what the type requires), in which case Swift
>>>>>> can continue to choose arbitrarily among the extensions, 
>>>>> 
>>>>> Surely that can't be the right long-term design?
>>>> 
>>>> When all of the extensions are unconstrained, it still doesn’t matter
>>>> to the user where the conformance goes. In truth, this rule extends to
>>>> groups of extensions that have the *same* constraints—it doesn’t
>>>> matter which one we pick.
>>> 
>>> Seems to me it does if they define different semantics!  What am I
>>> missing?
>> 
>> If they have the same constraints, they’ll have the same
>> overload-resolution behavior when picking declarations to satisfy
>> requirements.
> 
> Ya lost me here.

Consider this simplistic example:

protocol P {
  func f()
}
protocol Q: P { }
protocol R: P { }

struct X { }
extension X: P { }
extension X {
  func f() { … }
}

It doesn’t actually matter whether the declaration of X’s conformance to P is placed on the first extension or on the second extension. It’ll still use that same ‘f’ to satisfy P’s requirement. This is still to if we have two conditional conformances where the extensions have the same requirements.

struct Y<T> { }
extension T: P where T: P { }
extension T: P where T: P {
  func f() { … }
}

> 
>>> 
>>>>>> or
>>>>>> 
>>>>>> 2. All such extensions are constrained extensions, in which case the
>>>>>> program is ill-formed due to the ambiguity. The developer can
>>>>>> explicitly specify conformance to the protocol to disambiguate.
>>>>> 
>>>>> I'm really concerned about the understandability of this model.  It
>>>>> doesn't seem rational or consistent with other choices in the
>>>>> language. Now maybe I'm missing something, but in that case I think you
>>>>> haven't given a clear/precise enough description of the rules.  But the
>>>>> next section looks like it might clear things up for me...
>>>>> 
>>>>> ...(I'm sorry to say it didn’t)
>>>> 
>>>> While the exposition can be improved somewhat, placement of implied
>>>> conformances is fairly tricky and low-level. It will surface to the
>>>> user in cases where the user writes something like your
>>>> Comparable/Hashable example:
>>>> 
>>>>> extension Array : Comparable where T : Comparable {}
>>>>> extension Array : Hashable where T : Hashable {}
>>>> 
>>>> and the compiler needs to complain about the inability to place
>>>> ‘Equatable’ in a way that directs the user to the correct answer
>>>> without exposing them to the entire chain of reasoning we’re going
>>>> through. The diagnostic would need some thought:
>>>> 
>>>> 	error: ‘Array' requires an explicit conformance to ‘Equatable’
>>>> 	due to conflicting implied conformances (from ‘Comparable’ and
>>>> 	‘Hashable’)
>>> 
>>> If we're not making this illegal:
>>> 
>>>     extension Foo : Comparable { ... }
>>>     extension Foo : Hashable { ... }
>>>     // extension Foo : Equatable { ... }
>>> 
>>> I think it's going to be tough for people to understand why the
>>> overlapping implied conformances are a problem in the other case.
>> 
>> Given the negative feedback we got when I tried to require this
>> before, I don’t think it’s wise to require it except in those cases
>> where it is necessary for conditional conformances. And, of course, it
>> would be a source-breaking change to go back and require this for
>> non-conditional conformances.
> 
> I'm not saying we should make the above illegal!  I'm saying we need a
> better way to explain why the conditional case doesn't work.

Ah; that I agree with .

	- Doug

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


More information about the swift-evolution mailing list