Interesting comment about worries that you'd be dispatched to the least good SS version . A different language with different constraints, but when LINQ to Objects implementers needed to provide optimized c# implementations for some operations they chose a runtime type check to dispatch to the optimized version. For example, while API is exposed as IEnumerable<T> there are method implementations that check for ICollection<T> at runtime in order to hit more efficient implementations. So static dispatch is good, but win for collections often big enough to overcome a hit from dynamic dispatch.
On Sunday, October 2, 2016, plx via swift-evolution < swift-evolution@swift.org> wrote: > > On Sep 30, 2016, at 1:23 PM, Douglas Gregor <dgre...@apple.com > <javascript:_e(%7B%7D,'cvml','dgre...@apple.com');>> wrote: > > > This is purely anecdotal but I had a lot of utility code laying around > that I’d marked with notes like `// TODO: revisit once conditional > conformances are available`. > > When I was leaving those notes I was expecting to need overlapping > conformances often, but I reviewed them *before* replying and I actually > haven’t found an example where having overlapping conformances is both (1) > a significant win and also (2) a win in a way that’d be of broad, general > interest. > > - 80% have no real need for overlapping conditional conformances > - 15% might have “elegance gains” but nothing practically-significant > - 5% would *probably* see real gains but are likely not of broad interest > > …which wasn’t what I was expecting, but leaves me a lot more comfortable > without overlapping conformances for now than I was in the abstract. > > > Very interesting, thanks for doing this review! > > > I've taken the time to provide a bit more color on the 80/15/5 breakdown > because I don't see much discussion for this proposal in terms of concrete > situations...just theoretical concerns and theoretical possibilities. I > don't have any completed code either, but I have notes and to-do lists for > things I was planning to do, and I think seeing even some semi-concrete > scenarios might be helpful here. > > The "80%" are generally analogous to the `SomeWrapper` in the writeup; as > a concrete example, I was waiting on the availability of conditional > conformances to resume work on an emulation of structural unions, e.g. > something like: > > enum Sum2<A,B> { > case a(A) > case b(B) > } > > ...(and analogously for 3, 4, as-necessary). > > There's a very obvious way to write `extension Sum2 : Equatable where > A:Equatable, B:Equatable {}`...and at the time I set this aside, I was > expecting to also want to come back and have additional conformances for > things like `...where A:Equatable, B:AnyObject` (using `===` for comparing > `B`) and so on for other combinations. > > Upon revisiting such things in light of the proposal, I now think > differently: for this case it seems like a better long-term approach > anyways to stick to a single conformance and work with it like this: > > extension Sum2:Equatable where A:Equatable, B:Equatable { > // details elided > } > > /// Adaptor using `ObjectIdentifier` to implement `==`. > struct ObjectWrapper<Wrapped:AnyObject> : Equatable, Hashable { > let wrapped: Wrapped > } > > ...as upon reflection I really would prefer dealing with the hassle of > working with `Sum2<A,ObjectWrapper<B>>` in situations where -- in theory -- > `Sum2<A,B>` could do -- to the hassle of writing out 4+ conformances for > `Sum2` (and so on...even with nice code-gen tools that's going to be a lot > of bloat!). > > What changed my mind was tracing through the implications of conditional > conformances for the use-site ergonomics of adaptors like `ObjectWrapper` > above; what I mean is, suppose I have a protocol like this: > > protocol WidgetFactory { > associatedtype Widget > associatedtype Material > > func produceWidget(using material: Material) -> Widget > } > > ...then it's rather easy to simply write this type of boilerplate: > > extension ObjectWrapper: WidgetFactory where Wrapped: WidgetFactory { > typealias Widget = Wrapper.Widget > typealias Material = Wrapper.Material > > func produceWidget(using material: Material) -> Widget { > return base.produceWidget(using: material) > } > } > > ...which thus means I have the tools I need to make my use of wrappers > like the `ObjectWrapper` largely transparent at the use sites I care about; > e.g. I can write a single conditional conformance like this: > > extension Sum2: WidgetFactory > where > A:WidgetFactory, B:WidgetFactory, > A.Material == B.Material, > A.Widget == B.Widget { > > typealias Widget = A.Widget > typealias Material = A.Material > > func produceWidget(using material: Material) throws -> Widget { > switch self { > case let .a(aa): return aa.produceWidget(using: material) > case let .b(bb): return bb.produceWidget(using: material) > } > } > > } > > ...and it will apply even in situations where circumstances left me using > `ObjectWrapper` (or similar) on any of the type parameters to `Sum2` (e.g. > if I also needed an `Equatable` conformance for whatever reason). > > At least for now--when I'm still just revisiting plans and thinking about > it in light of the proposal--I really would prefer having a simpler > language and writing this type of boilerplate, than having a more-complex > language and writing the *other* type of boilerplate (e.g. the 4+ > `Equatable` conformances here, and so on for other situations). > > Note that I'm not claiming the above is the only use for overlapping > conditional conformances -- not at all! -- just that situations like the > above comprise about 80% of the things I was intending to do with > conditional conformances...and that after revisiting them expecting to be > troubled by the proposed banning of overlapping conformances, I'm now > thinking I'd wind up not using the overlapping-conformance approach in such > cases even if it were available. > > So that's the first 80%. > > Moving on, the next 15% are places where there's some forced theoretical > or aesthetic inelegance due to the lack of overlapping conformances, but > none of these seem to have any significant practical import. > > A representative case here is that I currently have the following pair: > > /// `ChainSequence2(a,b)` enumerates the elements of `a` then `b`. > struct ChainSequence2<A:Sequence,B:Sequence> : Sequence > where A.Iterator.Element == B.Iterator.Element { > // elided > } > > /// `ChainCollection2(a,b)` enumerates the elements of `a` then `b`. > struct ChainCollection2<A:Collection,B:Collection> : Collection > where A.Iterator.Element == B.Iterator.Element { > // ^ `where` is not quite right, see below > } > > ...and obviously conditional conformances will allow these to be > consolidated into a single `Chain2` type that then has appropriate > conditional conformances (and for which the cost/benefit for me will tip in > favor of adding conditional conformances to `BidirectionalCollection` and > `RandomAccessCollection`, also). > > On paper--e.g., theoretically--the lack of overlapping conformances leaves > in a few aesthetic issues...for example, at present `ChainCollection2` > actually has to be one of these: > > // "narrower" option: not all `A`, `B` can necessarily be used together: > struct ChainCollection2<A:Collection,B:Collection> : Collection > where > A.Iterator.Element == B.Iterator.Element, > A.IndexDistance == B.IndexDistance { > typealias IndexDistance = A.IndexDistance > } > > // "wasteful" option: theoretically in some cases we are "overpaying" > and > // using a stronger `IndexDistance`, but now we can use any `A` and `B` > struct ChainCollection2<A:Collection,B:Collection> : Collection > where A.Iterator.Element == B.Iterator.Element { > typealias IndexDistance = IntMax > } > > With overlapping conditional conformances you could have both: one > conformance that uses base collections' `IndexDistance` when possible, and > another that uses `IntMax` when necessary...but without conditional > conformances it's necessary to choose between the "narrower" approach or > the "wasteful" approach (preserving the status quo). > > If you're following along I'm sure you're aware that in this specific > case, this "choice" is purely academic (or purely aesthetic)...if you go > with the `IntMax` route there's almost always going to be between "no > actual difference" and "no measurable difference", so even if it *maybe* > feels a bit icky the right thing to do is get over it and stop making a > mountain out of an anthill. > > Note that I'm well aware that you can choose to see this as a concrete > instance of a more-general problem -- that the lack of overlapping > conformances would potentially leave a lot of performance on the table due > to forcing similar decisions (and in contexts where there *would* be a real > difference!) -- but speaking personally I couldn't find very much in my > "chores pending availability of conditional conformance" that both (a) fell > into this category and (b) had more than "aesthetic" implications. > > This brings me to that last 5% -- the handful of things for which > overlapping conformances have nontrivial benefits -- and my conclusion here > is that these tended to be things I doubt are of general interest. > > An example here is that I like to use a function that takes two sequences > and enumerates their "cartesian product", with the following adjustments: > > - no specific enumeration *ordering* is guaranteed > - does something useful even with infinite, one-shot sequences... > - ...meaning specifically that it will eventual-visit any specific pair > (even when one or both inputs are infinite, one-shot) > > ...(useful for doing unit tests, mostly), which to be done "optimally" > while also dotting all the is and crossing all the ts would currently > require at least 8 concrete types: > > - 4 sequences, like e.g.: > - UnorderedProductSS2<A:Sequence, B:Sequence> > - UnorderedProductSC2<A:Sequence, B:Collection> > - UnorderedProductCS2<A:Collection, B:Sequence> > - UnorderedProductCC2<A:Collection, B:Collection> > - 4 iterators (one for each of the above) > > ...since you need to use a different iteration strategy for each (yes you > don’t *need* 8 types, but I’m trying to “dott all is, cross all ts” here). > > In theory overlapping conditional conformances could be used to cut that > down to only 5 types: > > - 1 type like `UnorderedProduct<A:Sequence,B:Sequence>` > - the same 4 iterators from before, each used with the appropriate > conformance > > ...which *is* less code *and* seemingly provides nontrivial gains (the > `SS` variant must maintain buffers of the items it's already seen from each > underlying sequence, but the others have no such requirement). > > But, to be honest, even if those gains are realized, this is the kind of > situation I'm perfectly comfortable saying is a "niche" and neither broadly > relevant to the majority of Swift developers nor broadly relevant to the > majority of Swift code; if overlapping conformances were available I'd use > them here, but I'm not going to ask for them just to be able to use them > here. > > Also, I'm skeptical these gains would be realized in practice: between the > proposed "least specialized conformance wins" rule and Swift's existing > dispatch rules in generic contexts, it seems like even if overlapping > conformances *were* allowed and I *did* use them, I'd still wind up getting > dispatched to the pessimal `SS` variant in many cases for which I'd have > been hoping for one of the more-optimal versions. > > So between the niche-ness of such uses -- and their being 5% or less of > what I was hoping to do -- and my skepticism about how dispatch would pan > out in practice, I can't get behind fighting for overlapping conformances > at this time unless they'd be permanently banned by banning them now. > > As already stated, I do think that their absence *will* reveal some *true* > pain points, but I think it makes sense to adopt a "wait-and-see" approach > here as some more-targeted solution could wind up being enough to address > the majority of those future pain points. > > These are my more-detailed thoughts after looking at what I was planning > to do with conditional conformances once the became available. I realize it > doesn't touch on every conceivable scenario and every conceivable use, but > I want to reiterate that I did my review expecting to find a bunch of > things that I could use as justifications for why Swift absolutely should > have overlapping conditional conformances right now...but on actually > looking at my plans, I couldn't find anything for which I actually felt > that way. > > > - Doug > > >
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution