[swift-evolution] Renaming for Protocol Conformance
Callionica (Swift)
swift-callionica at callionica.com
Tue Aug 23 18:23:50 CDT 2016
The framework design guidelines were for ensuring code consistency and
necessarily included stylistic decisions. There was no ban on using
explicit interface implementation, just reasonable caution to the end goal
of consistency. This is what the framework design guidelines said in 2010:
https://msdn.microsoft.com/en-us/library/ms229034(v=vs.100).aspx
A web search suggests that the current version of the guidelines contains
no guidance on explicit interface implementation, but could be that I just
couldn't find it.
Explicit implementations were essential for moving collection interfaces
and others to generic versions - there's plenty of uses in the Microsoft
.Net framework implementation - so I would be surprised if there was a
feeling that the feature was a mistake, but you'd have to ask Anders and
others. (I don't know if TypeScript has a similar feature, but if not,
there are alternative explanations for it's absence than thinking it was a
mistake for C#).
You're correct that C# doesn't allow renaming: it requires the interface
name to be prefixed to the method name at the point of definition.
On Tue, Aug 23, 2016 at 4:03 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
> On Tue, Aug 23, 2016 at 5:51 PM, Callionica (Swift) <
> swift-callionica at callionica.com> wrote:
>
>> C# has this feature (called "explicit interface implementation" or
>> "explicit interface method implementation"). It's useful for implementing
>> multiple data-like interfaces that have common names like Name, ID, Title,
>> and Description. It's useful for keeping public interfaces clean. It's
>> useful for API evolution: sometimes you need to implement both IVersion1
>> and IVersion2 interfaces that have (as you would expect) similar method
>> names and semantics, but differences that still require different
>> implementations.
>>
>> There's a stack overflow topic which has various perspectives on it:
>> http://stackoverflow.com/questions/143405/c-sharp-interf
>> aces-implicit-implementation-versus-explicit-implementation
>>
>
> This is very interesting information. The OP there, dating from '08,
> states: "Microsoft's official guidelines (from first edition Framework
> Design Guidelines) states that using explicit implementations are not
> recommended." Is that, to your knowledge, still the consensus opinion from
> Microsoft? If the language designers of C# were to do it all over again,
> would this feature have been added at all?
>
> Two notable considerations. In this proposal, Jonathan is suggesting that
> we _rename_ protocol requirements; I don't think C# EIMI has that feature,
> so it would remain to be determined whether this new twist could be an
> improvement or a regression as compared to EIMI.
>
> Second, invoking an EIMI member requires creating an interface instance.
> In Swift, however, protocols that have Self or associated type requirements
> can only be used as generic constraints. It is unclear to me if or how the
> Swift type system would represent something analogous to an interface
> instance.
>
>
> On Tue, Aug 23, 2016 at 8:35 AM, Xiaodi Wu via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>>> On Tue, Aug 23, 2016 at 3:02 AM, Jonathan Hull <jhull at gbis.com> wrote:
>>>
>>>>
>>>> On Aug 22, 2016, at 11:32 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>>>>
>>>> On Mon, Aug 22, 2016 at 11:59 PM, Jonathan Hull via swift-evolution <
>>>> swift-evolution at swift.org> 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.
>>>
>>> Take a look at Eiffel’s ‘rename’ & ’select’ features for similar
>>>> functionality and use-cases.
>>>>
>>>> Ultimately, this is a step in the direction of having true mixins.
>>>>
>>>>
>>> Sure, maybe. I couldn't evaluate that claim. I'm inclined to favor the
>>> proposal, but it'd have to stand on its own merits, not as a step to an
>>> as-yet undesigned feature.
>>>
>>> I am fairly certain this affects the ABI though, so I thought I would
>>>> bring it up now.
>>>>
>>>>>
>>>>> If two protocols have methods/properties with the same name, but
>>>>> different signatures, we need a way to distinguish between them when
>>>>> attempting to conform to both.
>>>>>
>>>>> protocol A {
>>>>> var x:Int {get set}
>>>>> }
>>>>>
>>>>> protocol B {
>>>>> var x:Double {get set}
>>>>> }
>>>>>
>>>>
>>>> Methods can be overloaded that differ in arguments or return type, so
>>>> it seems like this problem mainly exists with *properties* that differ in
>>>> type--am I wrong?
>>>>
>>>>
>>>> There is also the case of functions with the same name and signature,
>>>> but different semantics. There may be no single implementation which
>>>> simultaneously satisfies the semantics for both protocols. By renaming one
>>>> of the functions, we are able to provide separate implementations for each
>>>> requirement (which allows both protocols to function as intended).
>>>>
>>>
>>> True. However, putting on my critical hat, this seems like we're
>>> stretching to provide support for an anti-pattern. It'd be nice to have an
>>> example where one runs into the motivating problem *and* where the proposed
>>> feature promotes a _better_ design than is currently possible, rather than
>>> making a bad design compile.
>>>
>>> At this point, I'm imagining scenarios where a user is trying to conform
>>> a type MyAnimal to both Biped and Quadruped, then worrying that `walk()`
>>> has two semantics: something has already gone deeply wrong IMO.
>>>
>>> [Yes, I know there are animals that can sometimes walk on two or four
>>> legs. The point here is that the protocols were clearly designed to model
>>> animals at a certain level of detail, while it appears that the user
>>> writing `MyAnimal` wants to model the animal at a different level of detail
>>> than either protocol was designed to handle. You might have specific qualms
>>> about this particular hypothetical, but I think you can pick out the
>>> general point that there is a much larger problem inherent in the design
>>> than the specific problem regarding two colliding method signatures.]
>>>
>>> There may also be functions in different protocols with different names
>>>> but the same semantics and signature. This will allow a single
>>>> implementation to satisfy both protocols without duplication.
>>>>
>>>
>>> This is a poor argument IMO. You can already implement foo() and then
>>> have bar() forward to foo() with trivial effort and really minimal
>>> boilerplate. It's an existing solution, and a more general solution because
>>> it doesn't require matching signatures. Also, it's a better solution IMO
>>> because it preserves the notion that a type T : Fooable, Barrable provides
>>> the full API guaranteed by Fooable and Barrable.
>>>
>>> Finally, we may want to rename an inherited default implementation to
>>>> avoid conflicting with another protocol's default implementation in cases
>>>> where we don’t want to override it.
>>>>
>>>
>>> Yes, I think this would be handy. I can't think of an existing way to do
>>> this, and I expect it might make a big difference in designing good
>>> protocols. So here, I think we have a strong argument.
>>>
>>> Again, though, could we find a concrete example of how this feature
>>> would promoter a better design of an actual type and/or protocol?
>>>
>>> One possibility is to allow a struct/class/enum to conform to the
>>>> protocol while renaming one (or both) of the clashing methods:
>>>>
>>>>>
>>>>> struct C: A,B {
>>>>> var x:Int
>>>>> var y:Double implements B.x
>>>>> }
>>>>>
>>>>> The conforming method/property would still have to have the same
>>>>> signature, but could have a different name (and parameter labels). It
>>>>> would also allow protocol methods which have identical signatures and
>>>>> semantics, but different names to be implemented using the same method (i.e
>>>>> ‘implements D.z & E.w’).
>>>>>
>>>>> When something is cast to the protocol (say ‘as B’), then calling the
>>>>> property (e.g. ‘x’) would end up calling the implementation of the renamed
>>>>> property ( ‘y’ in this example) on the conforming type.
>>>>>
>>>>
>>>> Reflecting on this proposed change, it occurs to me that something of
>>>> value would be lost, and I think that this something is actually rather
>>>> valuable:
>>>>
>>>> Today, when I see that a type conforms to (for example) Sequence, I
>>>> know that certain methods and/or properties exist on that type. Protocol
>>>> conformance guarantees a certain API, not just certain semantics.
>>>>
>>>>
>>>> It isn’t actually lost, however. When working with it as a Sequence
>>>> (for example), that API would be intact using the original names. It is
>>>> only when working with it as its own type that the renaming would have an
>>>> effect.
>>>>
>>>
>>> That is not my point. With this proposal, knowing that MyGreatType
>>> conforms to Sequence would no longer yield any information as to the
>>> MyGreatType API. That is definitely something lost and we should
>>> acknowledge that.
>>>
>>> Also, recall that Sequence has Self or associated type requirements. So:
>>>
>>> ```
>>> let m = MyGreatType()
>>> // There is nothing I can write here to use the Sequence API with `m`,
>>> IIUC;
>>> // however, depending on how this feature is designed, I *might* be able
>>> to
>>> // call a generic function that operates on a type `T : Sequence` and
>>> work
>>> // with `m` that way.
>>> ```
>>>
>>> Perhaps one way to mitigate this loss would be to have any renamed
>>>> members listed *in the declaration of conformance*, something like this
>>>> (with some additional bikeshedding):
>>>>
>>>> ```
>>>> struct MyGreatType : Sequence (count => length) {
>>>> // MyGreatType conforms to Sequence but renames `count` to `length`
>>>> }
>>>> ```
>>>>
>>>>
>>>> Yes, putting it in the conformance declaration is a definite
>>>> possibility we should consider.
>>>>
>>>>
>>>> I think we would also want a way to retroactively conform using
>>>>> existing properties/methods in an extension declaring conformance. Not
>>>>> sure what the best syntax for that would be. Off the top of my head
>>>>> (though I would love to have something with less cruft):
>>>>>
>>>>> extension D:B {
>>>>> @conform(to: B.x, with: D.y)
>>>>> }
>>>>>
>>>>> or maybe just:
>>>>>
>>>>> extension D:B {
>>>>> D.y implements B.x
>>>>> }
>>>>>
>>>>
>>>> If renamed members are declared along with protocol conformance, then
>>>> the syntax for retroactive modeling follows naturally:
>>>>
>>>> ```
>>>> extension D : B (x => y) { }
>>>> // again, the actual notation here is ugly
>>>> // but the underlying idea, I think, is worth considering
>>>> ```
>>>>
>>>>
>>>> Yup
>>>>
>>>> One thing I like about this is that it helps to solve the diamond
>>>> problem. ‘x’ could be a default implementation in B which D does not
>>>> override. I think this is an important case which my original proposal
>>>> didn’t address fully.
>>>>
>>>> We should keep bikeshedding the syntax though...
>>>>
>>>> Thanks,
>>>> Jon
>>>>
>>>>
>>>>
>>>
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160823/e32ed689/attachment.html>
More information about the swift-evolution
mailing list