Just to be explicit, are you meaning the following? When looking for a conformance for SomeWrapper: Equatable
(I think you missed a :
, BTW), we first see if Wrapped: Equatable
is satisfied, and then, if not, if Wrapped: HasIdentity
is satisfied. Meaning, if ConformsToBoth: Equatable & HasIdentity
, then SomeWrapper< ConformsToBoth>: Equatable
uses the first one.
This (let's call it "conformance chains" ala Instance Chains, maybe?) certainly seems like it may be restricted enough to avoid many of the problems with arbitrary overlapping conformances, e.g. for the ones listed in the conditional conformances proposal that you link to:
- To address all possible ambiguities, one has to write a conditional conformance for every plausible combination of overlapping requirements. To statically resolve all ambiguities, one must also cover nonsensical combinations where the two requirements are mutually exclusive (or invent a way to state mutual-exclusivity).
This doesn't apply to conformance chains, since they're just ordered. If one wants to do something special for an intersection, write an explicit one (e.g. the first line becomes extension SomeWrapper: Equatable where Wrapped: Equatable & HasIdentity
and the where Wrapped: Equatable
moves to an else
for the example you give) and put it first, but otherwise the behaviour is just picked up off the first one.
The compiler even knows enough about the where
clauses to notice mistakes like putting an intersection last, and so could flag it with an error/warning.
- It is no longer possible to uniquely say what is required to make a generic type conform to a protocol, because there might be several unrelated possibilities. This makes reasoning about the whole system more complex, because it admits divergent interfaces for the same generic type based on their type arguments. At its extreme, this invites the kind of cleverness we've seen in the C++ community with template metaprogramming, which is something Swift has sought to avoid.
This one is the least resolved, I think. For instance, given
func doSomethingSometimes<T>(x: SomeWrapper<T>, y: SomeWrapper<T>) {
if x == y {
print("the same")
}
}
how do we express the maximally-flexible bounds for this function? We want to be able to write bounds that mean "T: Equatable
or T: HasIdentity
". And, if we have that, then the ==
call is not able to be statically resolved: it can be either of SomeWrapper
s ==
functions.
Lastly, associated types could be a little tricky too. For an associated type, it seems like there's two basic options:
- all conformances have the same type
- each conformance has different type
Just thinking off the top of my head, it seems like we may want to explicitly support both of these. If we can guarantee that all values of an associated type AT
are the same, then X<T>.AT
(even if T
is a parameter constrained with a disjunction like doSomethingSometimes
) is interchangeable with the underlying type, and if it has different types, then it has to be treated as an opaque value, similar to T.AT
for a completely generic T
.
- All of the disambiguation machinery required at compile time (e.g., to determine whether one conditional conformance is more specialized than another to order them) also needs to implements in the run-time, as part of the dynamic casting machinery. One must also address the possibility of ambiguities occurring at run-time. This is both a sharp increase in the complexity of the system and a potential run-time performance hazard.
I think conformance chains resolves this one nicely too: there's currently a loop that runs over the conditional requirements checking them for a dynamic cast, and this could move to a nested loop within an outer loop over the possible sets of generic requirements. There's likely a little bit of fiddle with making sure the requirements end up in the right place: once we've found a conformance branch that works, we need to get the dynamic record that contains its implementations, not one of the other ones, but having everything in exactly one place makes that easier (it allows using static buffers of size "number of conformance branches", plus integer indices, etc.)