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

Vladimir.S svabox at gmail.com
Thu Sep 22 06:55:14 CDT 2016


Btw,..

On 20.09.2016 18:18, Nevin Brackett-Rozinsky via swift-evolution 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.

See how "my" suggestion regarding requiring a keyword for implementation of 
protocol requirement (but with `override` keyword instead of `implement`) 
can be very helpful here
(And Karl, seems like "your" suggestion will not help here - I mean we'll 
have to change bar() definition in B in case A implement its own bar())

In this case, initially, B.bar() must be marked with "override" as it is 
implementing protocol's requirement. *And* this will "protect" B class in 
future if class A will implement its own bar() method:

protocol Foo {
	func bar()
}

extension Foo {
	func bar() {
		print("I am bar.")
	}
}

class A:Foo {
}

class B:A {
	*override* func bar() { // currently protocol impelentation
		print("I am B.")
	}
}

and then if

class A:Foo {
	override func bar() {...}
}

,B's definition will not be changed.

Then we have a question regarding super.bar() inside B.bar(), i.e. should 
it be accessible or not. I believe we should consider if it could be 
implemented, so in case A has no bar(), super.bar() will call protocol's 
implementation, otherwise (if A contains bar()) - A.bar() will be called. 
Don't know if this is what very hard to implement.
Probably as first step super.bar() will not be allowed inside of protocol 
implementation.

Swift allows for protocols to have default *implementations* for 
requirements. And *also* Swift allows you to *not* implement requirement 
that has default implementation in conformed type.
When you don't implement such requirement in type - the type *inherits* 
that implementation. We have classical inheritance scheme like in 
inheritance of super type's method. When you override super type's method 
you have to use `override`. I believe overriding the inherited default 
implementation of requirement - also should be marked with `override`.

Then, Swift allows protocol to have extension in any source file in 
project. You can have no default implementation *at moment of writing* your 
type, but you can have such default implementation *at moment of 
compilation*. So, as soon as we *can't* know if protocol's requirement will 
*actually* have default implementation during the compilation - logically 
that we should use `override` to mark each method/prop defined as 
implementation of protocol, to suppose that there *is* default 
implementation which will be *overridden* by our custom implementation.

>
> 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 <mailto: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 <mailto: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 <mailto: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 <mailto:swift-users at swift.org>
>>         https://lists.swift.org/mailman/listinfo/swift-users
>>         <https://lists.swift.org/mailman/listinfo/swift-users>
>
>
>         _______________________________________________
>         swift-evolution mailing list
>         swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>         https://lists.swift.org/mailman/listinfo/swift-evolution
>         <https://lists.swift.org/mailman/listinfo/swift-evolution>
>
>
>
>     _______________________________________________
>     swift-evolution mailing list
>     swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>     https://lists.swift.org/mailman/listinfo/swift-evolution
>     <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
>


More information about the swift-evolution mailing list