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` 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. > 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. 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? 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
