Ergonomics: generic types conforming "in more than one way"

Hey Doug, I finally dug.
(sorry everybody)

Since the semantics of the depends on this check, I think the three of us would probably agree that it's more than just a preference: it's essential that it not matter, because those distinctions aren't present in the user model of the language. If that's what Joe meant by “semantically equivalent static and dynamic implementations” I 100% agree to its importance.¹

[schnipp good schtuff]

This is where you started to lose me, so I dumped all of the code into a file that I could compile and analyze. Then I made a slide deck to see the relationships.

Sure, because neither C nor E is more specialized. It's a little surprising that Array<X>() doesn't produce an ambiguity error in today's compiler.

If we model this case today by extending the conformance of Array to P with both conditions on its Element type… we end up getting an ambiguity error

I don't understand what you mean by “model this case today.” Are you just trying to trigger the same error that should have happened with Array<X>() above? Is this just a very complicated way of demonstrating what we can see by doing:

struct Y : Equatable, P, Q {} // same ambiguities

or is there something deeper that should be understood here?

Maybe the reason I am not surprised by any of this is that I'm really only thinking about semantics. I take it for granted that two types matching all the same conformances and where clauses should have their protocol requirements satisfied by the same extensions, even if one of the types is generic.

I consider that to be the correct behavior here, because there is no "best" answer.

Agreed.

I would be fine with the original conformance of Array to P (marked (D) at the beginning) becoming ambiguous and triggering an error at compile time.

With just (A), (B), (C), and (D) in the program? Can you explain what ambiguity you see there?

I think it would be a major problem with the design if this ambiguity could happen at run time.

Given that infinite types can cause arbitrarily complex generic types to be generated at run time, how would you go about proving that none of the possible combinations turn out to be like Array<X> in your example? Is there something about the limited expressivity of where clauses that makes it possible to make all those determinations at compile time?

You may remember, from our days long before Swift, that I'm not 100% sure this kind of ambiguity ought to be fatal. As you said, when one implementation of a requirement is more specialized than another, the first is “assumed to be better (faster, more accurate, etc.).” Why then should we not assume that when neither of two implementations is more specialized, it doesn't really matter which one is chosen (as long as it's deterministic)? If we can't diagnose all possible ambiguities at compile time, I think it would be very interesting to consider making an arbitrary choice and logging a warning.

To be clear, the fix here is not too complicated, and could possibly even be suggested by the compiler. You effectively need to provide another constrained implementation that is more specialized than both (C) and (E)

Sure. Presumably any number of sufficiently specialized extensions on Array could also be used to fix it?

Wow, after this you get into the cross-module stuff, and my brain is shutting down. I'll have to come back to this again later.


¹ I guess I misread what Joe wrote as “equivalent implementation of static and dynamic semantics.” Ah, semantics! Such a joy.

2 Likes