> Am 24.08.2016 um 21:35 schrieb Xiaodi Wu via swift-evolution > <[email protected]>: > >> On Wed, Aug 24, 2016 at 1:59 PM, Jonathan Hull <[email protected]> wrote: >> >>> On Aug 24, 2016, at 7:48 AM, Xiaodi Wu <[email protected]> wrote: >>> >>>> On Wed, Aug 24, 2016 at 3:39 AM, Jonathan Hull <[email protected]> wrote: >>>> >>>>> On Aug 23, 2016, at 8:35 AM, Xiaodi Wu <[email protected]> wrote: >>>>> >>>>>> On Tue, Aug 23, 2016 at 3:02 AM, Jonathan Hull <[email protected]> wrote: >>>>>> >>>>>>> On Aug 22, 2016, at 11:32 PM, Xiaodi Wu <[email protected]> wrote: >>>>>>> >>>>>>>> On Mon, Aug 22, 2016 at 11:59 PM, Jonathan Hull via swift-evolution >>>>>>>> <[email protected]> wrote: >>>>>>>> Hi everyone, >>>>>>>> >>>>>>>> We talked about this before when we were discussing mixins, and there >>>>>>>> seemed to be generally positive feelings towards it as a feature for >>>>>>>> the future. >>>>>>> >>>>>>> It's been some time now since the original discussion, so perhaps you >>>>>>> could refresh our collective memory (or at least, mine): although it >>>>>>> *seems* like this feature might be useful, I can't recall a concrete >>>>>>> use case where I've felt like I needed this feature--do you have some >>>>>>> examples? >>>>>> >>>>>> Ideally, the biggest use is that it helps to (partially) solve the >>>>>> diamond problem (and similar issues) by forcing/allowing disambiguation >>>>>> when there are multiple protocols being conformed to. This will become >>>>>> more of an issue if we allow protocols or extensions to add storage. >>>>>> Your proposed syntax actually does a better job of it than mine because >>>>>> mine was always shown as attached to some sort of implementation, >>>>>> whereas yours could potentially allow access to a default implementation >>>>>> under a new name. >>>>>> >>>>>> Other than that, it generally allows us to bypass/mitigate conflicts >>>>>> between protocols. In the current version, you are unable to conform to >>>>>> both protocols (either because it won’t compile or because you can’t >>>>>> satisfy the semantics of both protocols) without designing the protocols >>>>>> together to avoid conflicts. (I have definitely had to go back and >>>>>> rename/refactor properties on a protocol for this reason… which I >>>>>> couldn’t have done if I didn’t control both protocols). >>>>> >>>>> I understand something of the difficulty of confronting the diamond >>>>> problem. As I wrote above, I'm inclined to believe that this proposed >>>>> feature would help solve a real issue. However, the point I'm trying to >>>>> make is that, on reflection, I have never actually been hampered by the >>>>> lack of this feature, and so I'd like to continue the discussion to get a >>>>> fuller sense of just how impactful this proposal would be, both positive >>>>> and negative. >>>>> >>>>> It's true, of course, that if you control at least one of two protocols >>>>> (you don't need to control both protocols), it is trivially easy to cause >>>>> this problem to occur, but as you point out it is also possible to >>>>> resolve the problem by re-designing the protocol you control. I'm >>>>> inclined to think (without evidence, admittedly) that re-designing to >>>>> remove the conflict, where possible, would actually be the superior >>>>> option in most cases. >>>>> >>>>> My question was: have you actually run into a scenario that necessitates >>>>> the feature you propose because you controlled neither conflicting >>>>> protocol? I think it would strengthen the proposal greatly to have a >>>>> concrete, uncontrived example. >>>> >>>> Right now I commonly have to hand-namespace protocol methods/properties to >>>> avoid conflicts. So instead of having ‘var image:UIImage’ (which is the >>>> name which makes the most sense in the protocol’s context), I have ‘var >>>> protocolNameImage:UIImage’. There are lots of things which have common >>>> properties like ‘count’ which have to be called ‘somethingCount’ or >>>> ‘countOfSomething’. In the context of the protocol, these names are full >>>> of redundant words (especially when measured against the new naming >>>> guidelines). We are all used to doing this for Objective C, but it feels >>>> out of place in Swift. >>>> >>>> This will become a much more serious issue as the third-party code >>>> ecosystem grows. Without some capability like this, you will have >>>> frameworks which can’t be used together (or at least with the same >>>> object). I would hate to see a ‘best practice’ emerge of adding 3 letter >>>> prefixes to all protocol methods to get around compatibility issues. >>> >>> Ah, well this isn't exactly the diamond problem you're talking about here. >>> Instead, I think, we have a fundamental disagreement. I think I've been >>> told that this opinion of mine is 'insane'--but I hold to it: >> >> Well you asked for an additional example besides the diamond problem… so no >> it isn’t. I did include a diamond problem example further down though… > > Sorry, I wasn't asking for an example _besides_ the diamond problem. I was > asking for more information about a concrete scenario, diamond problem or > not, where an existing technique could not resolve the conflict (for > instance, a scenario when you controlled neither of two conflicting > protocols, and where no satisfactory alternative design existed that could > avoid conforming a single type to both protocols). > >> >>> Protocols are not merely a vehicle for delivering a reusable bag of code. >>> One of its most essential purposes is to constrain the shape or API of its >>> conforming types. Therefore, it is a feature, not a bug, that with every >>> choice of name in a protocol you foreclose the possibility of composing >>> that protocol with others that might have colliding names. >>> >>> Currently, if you the protocol vendor have made the decision that `image` >>> "makes the most sense in the protocol's context", you must have considered >>> whether it would be absurd for a conforming type to have another use for >>> `image`. If it would be absurd, then `image`
I find it a little bit strange to require from a protocol designer to foresee all future uses of a protocol. IMO protocols are not bags of code but encapsulate a certain (typically fine grained) semantic. How this semantic will be coupled with other semantics (i.e. protocols) is widely open. >>> is the appropriate name for your protocol requirement and any other word >>> would truly be redundant. But, if this is only one of many plausible >>> images, then `somethingImage` or `imageOfSomething` *is* the appropriate >>> name, and trying to shorten the name isn't at all consistent with Swift >>> guidelines but rather an incorrect attempt to prioritize brevity over >>> clarity. >> >> Most things that conform would have ‘image’, and it would have exactly the >> same semantics as my protocol. Thus their ‘image’ would provide conformance >> without additional work. But I have to worry about name collisions, so now >> I have to defensively call it ‘imageOfSomething', which they now have to >> implement to call their ‘image’ method. >> >> >>> What you're arguing is that protocol designers should be able to design >>> protocols without regard for how they will compose with others in >>> conforming types, relying on a new member-renaming feature instead. But, as >>> you point out, you can already use a protocol as a mere bag of code by >>> naming all members with unique, prefixed names, then have conforming types >>> forward their own choice of names to these. >> >> No, I am arguing that protocol authors should design protocols in the way >> which makes the behavior/semantics of the protocol the most obvious to the >> caller. 95% of the time there won’t be collisions, but occasionally there >> will be and we have to have a plan for that. >> >> >>> This member-renaming feature you propose would enhance the aesthetic >>> pleasure of the protocol designer, allowing simple names that don't ever >>> have to appear in the public API of a concrete type to be used for a >>> protocol member without placing any restrictions on the API of conforming >>> types. However, I don't see anything wrong with the current hand-prefixing >>> method being enshrined as "best practice" for the bag-of-code approach to >>> protocols. If, as you predict, a growing third-party code ecosystem makes >>> name collisions worse, then in fact having uniquely distinguishable >>> prefixed members would be less confusing than having conforming types >>> renaming protocol members as a matter of course. >> >> You are arguing that namespace collisions are a feature instead of a bug? >> Did you feel that way about ObjectiveC’s lack of name spacing? > > My argument is about protocols specifically: I understand that a major > feature of protocols is that they make guarantees regarding the API of > conforming types. In rare cases, two guarantees may conflict, but I do not > consider that conflict to be a bug per se, as it is the inevitable result of > what it means to have guarantees, i.e. it is part and parcel of the feature. > In order to provide a way of resolving conflicting requirements in protocols, > your solution eliminates the API-guaranteeing feature of protocols altogether. > > I can't comment about Objective-C, because I've never written a single line > of it. > >> I don’t think it is anywhere near as confusing as you suggest. As I >> mentioned before, if you cast it to the protocol, then the original names >> will still work. > > Except when you can't cast to a protocol existential, as is the case with any > protocol with Self or associated type requirements. This is a separate problem which will be solved once we have existential types. Furhermore the argument still holds when the protocol is being used as type constrained. > >> If you are trying to type the original name on the typed conformer, then >> showing the renamed version (with an indication of the renaming) in >> autocomplete should teach the change and clear up any confusion. > > Mine isn't an argument about usability or learnability. It's a > philosophical/design point: what are protocols for? My answer: among other > uses, for constraining the API of conforming types. Perhaps this view is > incompatible with the view that protocols should support additional > mixin-like features. I don't think this has anything to do with mixins. It is just the general problem of being able to combine protocols which have been designed independently from each other. > >>>> To take your example of walk(). Perhaps we have a protocol ‘Walkable’ >>>> which refers to any data structure where the nodes can be walked using the >>>> ‘walk()’ function. It is easy to imagine two different protocols A & B >>>> which specialize on this in different ways (say LinearWalkable & >>>> RandomWalkable), and both add some methods/properties and use those to >>>> provide efficient default implementations. At some point, you may run >>>> into a data structure which could easily be walked in both ways. >>>> >>>> As things are right now, you couldn’t inherit from both protocols. While >>>> you could add new ‘linearWalk()’ & ‘randomWalk()’ to the protocols >>>> respectively (cluttering their interface), there is still the issue of >>>> what to do when 'walk()’ is called. You can’t rename walk() in the >>>> originating protocols because it comes from their common ancestor. Much >>>> better to force one (or both) of the methods to be renamed on the >>>> conforming data structure. That keeps the interfaces of the protocols >>>> clean and makes the options available on the data structure clearer (e.g. >>>> ‘walk()’ & ‘randomWalk()’ ) >>>> >>>> What I have had to do in the current version is inherit from the original >>>> protocol and then copy and paste the default implementations from the >>>> specialized versions. Now my code has been duplicated and is harder to >>>> maintain. We can do better. >>> >>> I think Charles's solution is pretty nice, but he's right that the API >>> surface area will have to grow. I don't know his original use case, so I >>> don't know how ugly I'd find the final solution to be in that scenario. In >>> this particular example, I'd say that having `linearWalk()` and >>> `randomWalk()` distinguished seems pretty sensible and an overall win for >>> clarity. If the same vendor controlled all three protocols, then `Walkable` >>> could have the `walk()` requirement removed altogether for even more >>> clarity. >> >> So you would remove 'walk()' from Walkable to avoid the name collision in >> this one case, when ‘walk()’ is Walkable’s entire reason for being? > > No, `walk()` is not Walkable's entire reason for being. Protocols guarantee > semantics also. A `Walkable` protocol without any required members would > still have a reason for being: conforming types are walkable. > > On the other hand, given that Walkable certainly would have associated type > requriements, if you considered that `walk()` _was_ Walkable's entire reason > for being *and* you could rename `walk()` in any conforming type, how is that > different from not having a `walk()` requirement at all? Generic methods using a type constrained by Walkable can call `walk()` on it. The same will be possible for variables declared with existential types in the future and it already applies for protocols without associated type or Self requirements. -Thorsten > >> Also, you would lose polymorphism. >> >> >> >>> Also, just a hunch, but I suspect your hypothetical would never hold. Could >>> you envision how the requirements for RandomWalkable might be such that >>> it's possible to implement an efficient _default_ implementation of a >>> random walk for any of several conforming data structures, but only one of >>> these data structures is LinearWalkable, _and_ such a linear walk is >>> efficient using another _default_implementation for an overlapping but not >>> identical set of data structures? It's not mere trivia here, because the >>> crux of your argument is that there exist default implementations that >>> require copying and pasting into conforming types (and sufficiently >>> efficient default implementations so that copying and pasting is >>> appropriate rather than implementing a more efficient version). More likely >>> in diamond problem scenarios, I think, colliding members are going to be >>> properties or methods either without default implementations or than need >>> to supply more efficient versions of default implementations anyway. >> >> Based on what? There is a reason the diamond problem has a name (and a >> wikipedia entry). Charles just said he ran into a problem like this. I >> have run into it in the past as well. > > Sure, and returning to my question above: could you share details about where > you've run into this? > > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
