Tony makes an excellent point, and I agree. At some point in the future we should consider introducing a “transient” attribute for caches and other non-essential properties. That will make generated conformances more powerful while simultaneously reducing boilerplate.
Nevin On Tue, Sep 12, 2017 at 10:39 AM, Tony Allevato via swift-evolution < swift-evolution@swift.org> wrote: > > > On Mon, Sep 11, 2017 at 10:05 PM Gwendal Roué <gwendal.r...@gmail.com> > wrote: > >> >> There is this sample code by Thorsten Seitz with a cached property which >> is quite simple and clear : https://lists.swift.org/ >> pipermail/swift-evolution/Week-of-Mon-20170911/039684.html >> >> This is the sample code that had me enter the "worried" camp.' >> > > I really like Thorsten's example, because it actually proves that > requiring explicit derivation is NOT the correct approach here. (Let's set > aside the fact that Optionals prevent synthesis because we don't have > conditional conformances yet, and assume that we've gotten that feature as > well for the sake of argument.) > > Let's look at two scenarios: > > 1) Imagine I have a value type with a number of simple Equatable > properties. In a world where synthesis is explicit, I tell that value type > to "derive Equatable". Everything is fine. Later, I decide to add some > cache property like in Thorsten's example, and that property just happens > to also be Equatable. After doing so, the correct thing to do would be to > remove the "derive" part and provide my custom implementation. But if I > forget to do that, the synthesized operator still exists and applies to > that type. If you're arguing that "derive Equatable" is better because its > explicitness prevents errors, you must also accept that there are possibly > just as many cases where that explicitness does *not* prevent errors. > > 2) Imagine I have a value type with 10 Equatable properties and one > caching property that also happens to be Equatable. The solution being > proposed here says that I'm better off with explicit synthesis because if I > conform that type to Equatable without "derive", I get an error, and then I > can provide my own custom implementation. But I have to provide that custom > implementation *anyway* to ignore the caching property even if we don't > make synthesis explicit. Making it explicit hasn't saved me any work—it's > only given me a compiler error for a problem that I already knew I needed > to resolve. If we tack on Hashable and Codable to that type, then I still > have to write a significant amount of boilerplate for those custom > operations. Furthermore, if synthesis is explicit, I have *more* work > because I have to declare it explicitly even for types where the problem > above does not occur. > > So, making derivation explicit is simply a non-useful dodge that doesn't > solve the underlying problem, which is this: Swift's type system currently > does not distinguish between Equatable properties that *do* contribute to > the "value" of their containing instance vs. Equatable properties that *do > not* contribute to the "value" of their containing instance. It's the > difference between behavior based on a type and additional business logic > implemented on top of those types. > > So, what I'm trying to encourage people to see is this: saying "there are > some cases where synthesis is risky because it's incompatible with certain > semantics, so let's make it explicit everywhere" is trying to fix the wrong > problem. What we should be looking at is *"how do we give Swift the > additional semantic information it needs to make the appropriate decision > about what to synthesize?"* > > That's where concepts like "transient" come in. If I have an > Equatable/Hashable/Codable type with 10 properties and one cache property, > I *still* want the synthesis for those first 10 properties. I don't want > the presence of *one* property to force me to write all of that boilerplate > myself. I just want to tell the compiler which properties to ignore. > > Imagine you're a stranger reading the code to such a type for the first > time. Which would be easier for you to quickly understand? The version with > custom implementations of ==, hashValue, init(from:), and encode(to:) all > covering 10 or more properties that you have to read through to figure out > what's being ignored (and make sure that the author has done so correctly), > or the version that conforms to those protocols, does not contain a custom > implementation, and has each transient property clearly marked? The latter > is more concise and "transient" carries semantic weight that gets buried in > a handwritten implementation. > > Here's a fun exercise—you can actually write something like "transient" > without any additional language support today: https://gist.github.com/ > allevato/e1aab2b7b2ced72431c3cf4de71d306d. A big drawback to this > Transient type is that it's not as easy to use as an Optional because of > the additional sugar that Swift provides for the latter, but one could > expand it with some helper properties and methods to sugar it up the best > that the language will allow today. > > I would wager that this concept, either as a wrapper type or as a built-in > property attribute, would solve a significant majority of cases where > synthesis is viewed to be "risky". If we accept that premise, then we can > back to our slice of pie and all we're left with in terms of "risky" types > are "types that contain properties that conform to a certain protocol but > are not really transient but also shouldn't be included verbatim in > synthesized operations". I'm struggling to imagine a type that fits that > description, so if they do exist, it's doubtful that they're a common > enough problem to warrant introducing more complexity into the protocol > conformance system. >
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution