> 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

Reply via email to