[swift-evolution] [swift-users] [swift-user]Unexpected behavior of protocol extension.

Zhao Xin owenzx at gmail.com
Tue Sep 20 19:24:49 CDT 2016


I understand your point on "adding additional `override` to
re-implementation of default method in protocol extension". However, I
don't stand for it.

If you use `override`, that means `B` could use `super.bar()` to get the
original `bar()` implementation. Then if  `class A` implement `bar()`
someday. The `super.bar()` call in `class B` will be ambitious, it could be
`A.bar()` or `Foo.bar()`. That ambiguity is the same as the multiple object
inherences languages like c++. We all know that Swift can only inherit to
one object. That is why the behavior is what it is now.

Zhaoxin

On Tue, Sep 20, 2016 at 11:18 PM, Nevin Brackett-Rozinsky <
nevin.brackettrozinsky at gmail.com> wrote:

> I think there is a deeper issue that may be worth exploring here.
>
> Notably, when one class presents a member function, its subclasses ought
> to use “override” when they reimplement that method themselves, regardless
> of where the superclass’s version comes from.
>
> In the original post, the class “A” expresses (by conforming to protocol
> “Foo”) that it has a member function “bar()”, and “B” is a subclass of “A”
> which wants its own definition of “bar()”.
>
> It seems to me that “B” should not care whether “A” rolled its own
> implementation of “bar()” or used the default implementation provided by
> “Foo”.
>
> From the perspective of “B”, its superclass “A” promises to have a member
> function “bar()”, so “B” should need to use the `override` keyword just
> like it would when overriding any other method.
>
> To illustrate this more clearly, suppose that “Foo” and “A: Foo” are
> defined in a 3rd-party library, while “B: A” is written in a client module.
>
> If the library changes to give “A” its own custom implementation of
> “bar()”, that *should not* affect client code—because the class “A” still
> conforms to “Foo” so it still is known to have a “bar()” method—but right
> now it *does*:
>
> With the status quo, the simple change of moving a function between a
> protocol extension and a conforming class currently requires downstream
> source-code modifications in clients (in this case, adding `override` to
> “B.bar()”).
>
> I propose that `override` should be required in subclasses on any method
> which the superclass proffers as a customization point, no matter the
> provenance of that claim.
>
> Nevin
>
>
>
> On Tue, Sep 20, 2016 at 8:01 AM, Zhao Xin via swift-evolution <
> swift-evolution at swift.org> wrote:
>
>> Thank you to  Игорь Никитин and Adrian Zubarev. Now I can convince myself
>> for this behavior.
>>
>> As Adrian's example shows, there is no `bar()` implemented in `class A`,
>> so there is no `override func bar()` in class B`. My thought that  `self.bar()`
>> would do the same as `(self as! B).bar()`, is called "dynamic binding",
>> which is basing on the `override`. Since there is no `override`, there is
>> no "dynamic binding". I thought "dynamic binding" was basing on dynamic
>> type of `self`. It was not. I was wrong.
>>
>> The behavior is clear now. In class A's `self.bar()`, the runtime finds
>> that there is no implementation of `bar()` in `class A`, so it calls the
>> `bar` in protocol extension. In class A's `(self as! B).bar()`, as `class
>> B` contains the implementation of `bar()`, the runtime calls it.
>>
>> Zhaoxin
>>
>> On Tue, Sep 20, 2016 at 4:21 PM, Adrian Zubarev via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>>> I can’t tell you the reason, but to me it feels like it’s doing the
>>> following thing:
>>>
>>> + - - (Type: B) - - +
>>> |                   |
>>> | func bar()    + - + (Type: A) - - +                        < - - - - - - - - - - - - - - - - - - - - +
>>> |               |                   |                                                                  |
>>> + - - - - - - - + func output() + - + (Protocol: Foo) - - +  — - self.bar() - + - (self as! B).bar() - +
>>>                 |               |                         |                   |
>>>                 + - - - - - - - + (default) func bar()    |  <- - - - - - - - +
>>>                                 |                         |
>>>                                 + - - - - - - - - - - - - +
>>>
>>> class A:Foo {
>>>
>>>     func bar() {}
>>>
>>>     func output() {
>>>         print(type(of:self))
>>>         self.bar()
>>>         (self as! B).bar()
>>>     }
>>> }
>>>
>>> class B:A {
>>>
>>>     override func bar() {
>>>         print("I am B.")
>>>     }
>>> }
>>>
>>> Would solve this temporarily.
>>>
>>> And there we are again with the same discussion if custom implementation
>>> of protocol members, which have default implementation, should have the
>>> override keyword or not.
>>>
>>> Imagine your code like this (not valid code):
>>>
>>> protocol Foo {
>>>     func bar()
>>> }
>>>
>>> extension Foo {
>>>     func bar() {
>>>         print("I am bar.")
>>>     }
>>> }
>>>
>>> class A : Foo {
>>>
>>>     func output() {
>>>
>>>         print(type(of:self))
>>>         default.bar() // fallback an call directly the default implementation whenever needed
>>>         self.bar() // will print "I am bar." on A().output() but should print "I am B." if Self == B
>>>         (self as! B).bar()
>>>     }
>>> }
>>>
>>> class B : A {
>>>
>>>     override func bar() {
>>>         print("I am B.")
>>>     }
>>> }
>>>
>>> I still think default implementations should be called through something
>>> like default. + whenever you override a default implementation you’d
>>> need override. There is a discussion going on: Mark protocol methods
>>> with their protocol. I clearly did not solved your issue, but I might
>>> have wake your interest to participate. ;)
>>>
>>>
>>>
>>> --
>>> Adrian Zubarev
>>> Sent with Airmail
>>>
>>> Am 20. September 2016 um 04:13:22, Zhao Xin via swift-users (
>>> swift-users at swift.org) schrieb:
>>>
>>> See below code.
>>>
>>> protocol Foo {
>>>
>>>     func bar()
>>>
>>> }
>>>
>>>
>>> extension Foo {
>>>
>>>     func bar() {
>>>
>>>         print("I am bar.")
>>>
>>>     }
>>>
>>> }
>>>
>>>
>>> class A:Foo {
>>>
>>>     func output() {
>>>
>>>         print(type(of:self)) // prints "B".
>>>
>>>         self.bar() // prints "I am bar."
>>>
>>>         (self as! B).bar() // prints "I am B."
>>>
>>>     }
>>>
>>> }
>>>
>>>
>>> class B:A {
>>>
>>>     func bar() {
>>>
>>>         print("I am B.")
>>>
>>>     }
>>>
>>> }
>>>
>>>
>>> let b = B()
>>>
>>> b.output()
>>>
>>>
>>> I thought `self.bar()` would do the same as `(self as! B).bar()`. It
>>> didn't. In my opinion,  `type(of:self) is B.type`, so they should be the
>>> same, shouldn't they?
>>>
>>>
>>> Zhaoxin
>>> _______________________________________________
>>> swift-users mailing list
>>> swift-users at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-users
>>>
>>>
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>>>
>>
>> _______________________________________________
>> 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/20160921/0cd466df/attachment.html>


More information about the swift-evolution mailing list