> On Oct 2, 2016, at 8:56 AM, Callionica (Swift) via swift-evolution > <swift-evolution@swift.org> wrote: > > 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.
Yep, and Swift's specialization optimization already knows how to fold `is` and `as` checks after specializing a generic, so checking for a specific type or sub-protocol and jumping into a more optimized implementation for that subtype "dynamically" is still likely to be optimized away. -Joe > On Sunday, October 2, 2016, plx via swift-evolution > <swift-evolution@swift.org <mailto: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
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution