Recently, @Alejandro launched the Tuples Conform to Equatable pitch, along with a proposed implementation.
This thread, here, is for purposes of discussing the long-term tradeoffs that may result from the near-term adoption of built-in, special-case automatic protocol conformance.
Background
In short, the original pitch was to create a special-case, compiler-level mechanism to generate Equatable
conformance for all tuples whose elements themselves conform to Equatable
. The conformance would be automatic, without any means to opt-in or opt-out.
There exists a fairly wide-spread and long-standing desire to enable protocol conformance for tuples and perhaps other structural types, and especially so with respect to ubiquitous protocols, like Equatable
, Comparable
, Hashable
and Codable
. Presently, we are not in a position to facilitate custom protocol conformance as a general feature for tuples or other structural types. It is anticipated that that sort of feature will be created, eventually.
It is apparent that there would be some long-term implications to creating a near-term, special-case feature ("special case behavior"). For instance, when we gain the ability to provide custom protocol conformance for tuples, how will we migrate away from the special case behavior, and will there be conflict? More generally, will adopting this sort of special case behavior tend to constrain future lines of evolution?
Expansion of Proposal
The discussion in the original pitch thread, led to the Core Team, through @Joe_Groff, expressing the opinion:
We agree that the lack of these conformances is a significant pain point in the language and that it's worth considering introducing special case behavior until more general mechanisms for extending structural types are available.
As part of that same post, the Core Team indicated the desire to expand the proposal to include Equatable
, Comparable
and Hashable
, suggested that Codable
should be explored, and suggested that Hashable
also be discussed as a special-case conformance for other structural types.
Call for Discussion of Tradeoffs and Long-term Liabilities
The Core Team post concluded with a request for the sort of discussion contemplated by this thread (emphasis added):
In the ensuing discussion, we'd like to see attention paid to the long-term compatibility tradeoff of special-casing these conformances today, vs. what future language directions such as variadic generics may enable. If we accept any of these proposals, we would be committing to preserving compatibility with the source-level behavior, and on ABI-stable platforms, any runtime behavior and ABI requirements this introduces, even when more general mechanisms get introduced in the future. It would be good to explore and enumerate what these long-term liabilities are so that we know what we would be committing to.
The original thread included some preliminary discussion of the possible long-term implications and tradeoffs of adopting the special case behavior. This thread is for purposes of eliciting community-wide brainstorming to identify and discuss the potential tradeoff and implications.
Summary of Points from Original Thread
For convenience, and without intending to limit the scope of discussion in this thread, the following is a summary of some long-term-implication/trade-off points discussed in the original thread. Please pardon me if I inadvertently have omitted any contributions from the original thread.
In the pitch itself, @Alejandro noted the eventual divergence between the special case behavior and generalized protocol conformance for tuples:
In the future when we have proper tuple extensions along with variadic generics and such, implementing
Equatable
for tuples will be trivial and I imagine the standard library will come with a conformance for tuples. When that happens all future usage of that conformance will use the standard library's implementation, but older clients that have been compiled with this implementation will continue using it as normal.
In the ensuing discussion, @Alejandro cautioned against allowing work on special case behavior to slow progress towards generalized protocol conformance for tuples:
It's also important to state that we shouldn't wait for all of these magical conformances before working on proper implementations. This proposal does, however, allow newer conformances to be added fairly easily, but each conformance should be carefully considered.
@xwu raised the question of special case behavior leading to a potential ABI-break roadblock. The original thread does not contain an answer to, or even much discussion of, the question. Certainly, the question needs to be answered as definitively as possible.
Question—if this is accepted, and a resilient library makes use of magically generated tuple equality, will it be an ABI-breaking change in a future version of Swift to implement a custom conformance when it becomes possible to do so?
In considering the near-term benefits of adopting special case behavior, @DevAndArtist lamented that many users may not be able to benefit from the behavior in the near term:
One thing that makes me sad is that this requires new runtime support, so even if accepted I wouldn't be able to use it for a long long time.
To that same point, @Joe_Groff discussed the possibility of backward deployment to older runtimes:
One important implementation concern, though, is how to deal with backward deployment to older runtimes where tuples do not have any protocol conformances. There are couple of possibilities I can think of:
- The tuple-equatable conformance is only available when targeting an OS with Swift 5.2 or later. This wouldn't require any backward deployment hackery, but would likely require us to design and implement the general feature of how conditionally available protocol conformances work in the language.
- We use the backward compatibility library to backward-deploy this functionality into binaries targeting older OSes. This would allow users to take advantage of the functionality without restrictions*, but would require that we actually have enough hooks in the 5.0 and 5.1 runtimes that can be replaced to understand tuple conformances. In theory, it seems like this is possible, since
swift_conformsToProtocol
ought to be hookable, and we could provide copies of the runtime data structures for the conformance in the compatibility library, but it would require implementation effort to make sure this is actually possible.(*And the hooks have limitations of their own—they only take effect when linked into the main binary, not dylibs, so dynamic libraries that want to be distributed for use with older Swift toolchains that don't have the compatibility library would still not be able to take advantage of the new functionality.)
As food-for-thought about the unexpected ways that evolution might occur, I note that @DevAndArtist renewed his desire to explore treating tuple-types as structs, and asked whether the same is possible while maintaining source compatibility:
I mentioned this multiple times in different threads that personally I would find it cool if we had tuples as a struct while the general tuple syntax would become a tuple literal syntax which could also be used for other types such as for the python lib I believe.
Can we once and for all debunk if we could make this happen and keep source compatibility in the future. Obviously we would need variadic generics and at least generic parameter type labels.
struct Tuple<variadic Element>: ExpressibleByTupleLiteral { ... } extension Tuple: Equatable where Element: Equatable { ... } extension Tuple: Hashable where Element: Hashable { ... } extension Tuple: Encoding where Element: Encoding { ... } extension Tuple: Decoding where Element: Decoding { ... }
This would also allow for single element labeled tuples as we would have an unambiguous syntax (e.g.
Tuple<label: Int>
) and allow the average swift user to extend the type with custom protocol where appropriate.
@Tino offered some thoughts about future directions:
Imho adding conformances for tuples is a good idea, but I wish there was a bigger plan what should happen in the long run, instead of allowing some basic protocols in an ad hoc fashion.
- Will tuple conformance always stay a special-case feature?
- Will it be limited to protocols that only define operators (and some special cases)?
- Or are we going to see
extension (Int, Int): SomeProtocol
in the future?I can't see any downsides for the last option, as it fits great into my model of tuples as anonymous structs - but I know that for every possible change, you can find someone who opposes ;-)
Thinking about this, I might have found a downside myself ;-): When you allow custom conformances, you can have incompatible implementations - this does not happen when everything is handled by the compiler. But the same problem already exists for other types, so imho that's not a dealbreaker. It's still worse for tuples, though...
Finally, @Joe_Groff broadly observed (emphasis added):
I wouldn't personally expect much contention from the high-order idea that tuples have these protocol conformances, but adding them as a special case does have interactions with future language directions (allowing structural types to conform to protocols, variadics, and such) that need to be considered, ....
And, so, discussion has been invited.