[swift-evolution] [swift-users] [swift-user]Unexpected behavior of protocol extension.
Nevin Brackett-Rozinsky
nevin.brackettrozinsky at gmail.com
Tue Sep 20 10:18:22 CDT 2016
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/20160920/236349f2/attachment.html>
More information about the swift-evolution
mailing list