This really needs some elaboration, because this is an area where implementation constraints have a significant impact on what is possible with the language design. The current implementation model is that a protocol conformance is described by a "witness table", which is a mapping of each protocol requirement to its "witness", i.e., the one declaration that satisfied that particular requirement. With generic types, that witness has to be one that works for any generic arguments. To get a different witness for different generic arguments, which is fundamentally what this thread is about, requires the Swift runtime to inspect the capabilities of the actual argument types dynamically, and perform some decision procedure to pick the best witness. There are effectively two places where we can introduce a decision procedure that works with the current runtime:
- Within the implementation of a function itself. For example, if a single function had multiple bodies (e.g., each with a different
where
clause), the decision procedure would be at the beginning of the witness function. - When the witness table for a concrete type's conformance to a protocol is realized (e.g.,
Array<Int>
conforming toP
in @dabrahams's example). Here, the decision procedure would be in some runtime hooks we have during witness table realization. We can make decisions for each protocol requirement (independently).
Now, the fact that this decision process occurs at runtime implies a few things about our design constraints:
-
The process should be fast. We'll have to accept some dynamic lookups (e.g., does
Int
conform toEquatable
?), but we probably have to avoid global lookups that produce an unknown set of potential candidates to rank. -
The process should never fail. Overload resolution and similar ranking metrics have ambiguities. We should either define away those ambiguities (i.e., there is always a single best answer) or we need some kind of fallback (warn and pick at random? pick what today's language picks?).
Doug