Overriding protocol default implementation


(Robert Ryan) #1

As we all know, if you override the default implementation of a protocol, if you call the method from the protocol reference, the protocol’s default implementation will be called, not the override of that method. For example:

    protocol Fooable {
        // this is intentionally blank
    }

    extension Fooable {
        func foo() {
            print("foo")
        }
    }

    class FooBar: Fooable {
        func foo() {
            print("foobar")
        }
    }

    func performFoo(object: Fooable) {
        object.foo()
    }

    let foobar = FooBar()
    performFoo(object: foobar) // prints “foo”, not “foobar"

But, the behavior changes you change the default method to be part of the protocol, itself:

    protocol Fooable {
        func foo()
    }

    // the rest as above

    performFoo(object: foobar) // prints “foobar”

I would like to propose that any class that overrides a protocol’s default implementation where the method is not part of the protocol, itself, results in a warning. I’m hard pressed to think of a situation where you’d want the current Swift 3 behavior of the first example (where you can override a protocol default implementation but you only want that override used when you reference the class itself, but not when you reference the protocol as a type). If there are such examples, then add a “build setting” to allow you to turn off this warning, or add some keyword to the declaration of the default implementation that indicates that you’re allowing it to be overridden, but protocol types won’t use it (e.g. nondynamic). Personally, I’d just add the warning and call it a day (because I don’t know why you’d ever want the current Swift 3 behavior).


(David Waite) #2

I’m hard pressed to think of a situation where you’d want the current Swift 3 behavior of the first example (where you can override a protocol default implementation but you only want that override used when you reference the class itself, but not when you reference the protocol as a type).

Thats easy - the extension methods are not related to protocol conformance. The type implementing the protocol may not even know there *is* an extension method, or that method could be added either later or by yet other third party code.

As such, the type may not be overriding the extension method at all - the protocol-implementing types may have their own method with a conflicting name doing similar (or not) logic. It would not be appropriate for someone calling the protocol extension method to get different behavior simply because a type declared a similar named method.

In other words, without the method being declared on the protocol, override behavior between an extension and type would be similar to unsafe “duck” typing. There is no agreement by the type that they are conforming to extension behavior, only to the protocol’s behavior.

IMHO the issue actually goes the other way
- there is no way to indicate that an extension method to a protocol is meant to declare ‘default’ behavior, e.g. represents a partial implementation for types conforming to the protocol.
- there is no way to indicate that a type property/method ‘implements’ the functionality of a protocol.

The lack of a distinction between extensions declaring random methods and declaring default implementation behavior is obviously an element of confusion. Both points become an issue if the protocol method signatures ever changes - the extension could no longer be providing a default implementation, or a type may silently switch from their own implementation to the default implementation provided by the extension.

-DW

···

On Feb 10, 2017, at 12:41 PM, Robert Ryan via swift-evolution <swift-evolution@swift.org> wrote:

If there are such examples, then add a “build setting” to allow you to turn off this warning, or add some keyword to the declaration of the default implementation that indicates that you’re allowing it to be overridden, but protocol types won’t use it (e.g. nondynamic). Personally, I’d just add the warning and call it a day (because I don’t know why you’d ever want the current Swift 3 behavior).

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Adrian Zubarev) #3

Ugh, this is a bad one, nice catch. I personally never bumped into that, but I’ll be careful from now on that one. Thanks for sharing it.

I’m interested to see the outcome of this pitch. :slight_smile:

···

--
Adrian Zubarev
Sent with Airmail

Am 10. Februar 2017 um 20:41:57, Robert Ryan via swift-evolution (swift-evolution@swift.org) schrieb:

As we all know, if you override the default implementation of a protocol, if you call the method from the protocol reference, the protocol’s default implementation will be called, not the override of that method. For example:

protocol Fooable \{
    // this is intentionally blank
\}

extension Fooable \{
    func foo\(\) \{
        print\(&quot;foo&quot;\)
    \}
\}

class FooBar: Fooable \{
    func foo\(\) \{
        print\(&quot;foobar&quot;\)
    \}
\}

func performFoo\(object: Fooable\) \{
    object\.foo\(\)
\}

let foobar = FooBar\(\)
performFoo\(object: foobar\)      // prints “foo”, not “foobar&quot;

But, the behavior changes you change the default method to be part of the protocol, itself:

protocol Fooable \{
    func foo\(\)
\}

// the rest as above

performFoo\(object: foobar\)     // prints “foobar”

I would like to propose that any class that overrides a protocol’s default implementation where the method is not part of the protocol, itself, results in a warning. I’m hard pressed to think of a situation where you’d want the current Swift 3 behavior of the first example (where you can override a protocol default implementation but you only want that override used when you reference the class itself, but not when you reference the protocol as a type). If there are such examples, then add a “build setting” to allow you to turn off this warning, or add some keyword to the declaration of the default implementation that indicates that you’re allowing it to be overridden, but protocol types won’t use it (e.g. nondynamic). Personally, I’d just add the warning and call it a day (because I don’t know why you’d ever want the current Swift 3 behavior).

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Anton Zhilin) #4

I’d even suggest to force final for such methods:

protocol Fooable { }
extension Fooable {// func foo() { } // error: must be declared as final
    final func foo() { } // ok
}


(Adrian Zubarev) #5

I’d be in favor if we could look at this topic further - Generics Manifesto: Allowing subclasses to override requirements satisfied by defaults.

In my world this should look like this:

protocol P {
  func foo()
}

extension P {
  func foo() { print("P") }
}

class C : P {
  // gets the protocol extension's
   
  // or override it
  override func foo() {
     
    // do your work or optionally call the default implementation
    default.foo() // Notice how `default` suits here better than `super`
  }
}

class D : C {
     
  override func foo() {
     
    // #1 - if `C` has not overridden `foo()`
    print("D")
    // or
    default.foo()
     
    // #2 - if `C` has overridden `foo()`
    print("D") // ignore everything
    // or call `super`
    super.foo()
    // or even `default`
    default.foo()
  }
}
C does not necessarily need to be a class, that should be the same behavior for value types as well.

Additional to that we could make default implementations final so that you cannot override them from the conforming type at all.

Having this much of power and flexibility would be simply great.

Plus the mentioned behavior from the current thread should not even exist IMO.

···

--
Adrian Zubarev
Sent with Airmail

Am 10. Februar 2017 um 21:29:46, Anton Zhilin via swift-evolution (swift-evolution@swift.org) schrieb:

I’d even suggest to force
final for such methods:

protocol Fooable { }

extension Fooable {
// func foo() { } // error: must be declared as final
    final func foo() { } // ok
}
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Goffredo Marocchi) #6

I remain very unconvinced that type casting should change code being executed...

···

Sent from my iPhone

On 10 Feb 2017, at 21:04, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 10, 2017, at 12:41 PM, Robert Ryan via swift-evolution <swift-evolution@swift.org> wrote:

I’m hard pressed to think of a situation where you’d want the current Swift 3 behavior of the first example (where you can override a protocol default implementation but you only want that override used when you reference the class itself, but not when you reference the protocol as a type).

Thats easy - the extension methods are not related to protocol conformance. The type implementing the protocol may not even know there *is* an extension method, or that method could be added either later or by yet other third party code.

As such, the type may not be overriding the extension method at all - the protocol-implementing types may have their own method with a conflicting name doing similar (or not) logic. It would not be appropriate for someone calling the protocol extension method to get different behavior simply because a type declared a similar named method.

In other words, without the method being declared on the protocol, override behavior between an extension and type would be similar to unsafe “duck” typing. There is no agreement by the type that they are conforming to extension behavior, only to the protocol’s behavior.

IMHO the issue actually goes the other way
- there is no way to indicate that an extension method to a protocol is meant to declare ‘default’ behavior, e.g. represents a partial implementation for types conforming to the protocol.
- there is no way to indicate that a type property/method ‘implements’ the functionality of a protocol.

The lack of a distinction between extensions declaring random methods and declaring default implementation behavior is obviously an element of confusion. Both points become an issue if the protocol method signatures ever changes - the extension could no longer be providing a default implementation, or a type may silently switch from their own implementation to the default implementation provided by the extension.

-DW

If there are such examples, then add a “build setting” to allow you to turn off this warning, or add some keyword to the declaration of the default implementation that indicates that you’re allowing it to be overridden, but protocol types won’t use it (e.g. nondynamic). Personally, I’d just add the warning and call it a day (because I don’t know why you’d ever want the current Swift 3 behavior).

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #7

Robert, this is a tricky part of Swift. I raised the same point in my very
first email to this list a year and a half ago, and the response was that
no default/implement/override keyword was desired and that this behavior is
intentional. It has since been raised multiple times on this list to the
same conclusion.

The idea here is that protocol extension methods that are not protocol
requirements are statically dispatched and can be shadowed but not
overridden. If you read through some of the Swift-Evolution proposals,
you'll see that there are several designs that actually seek to take
advantage of this behavior. I can't recall which of these were actually
implemented in that way, but it does indeed show that the behavior has its
uses.

Many have suggested some sort of keyword to distinguish overriding and
shadowing. However, every such proposed design thus far breaks retroactive
conformance in some subtle way. It's hard to search the archives, but there
are hundreds of emails on this topic.

···

On Fri, Feb 10, 2017 at 15:57 Goffredo Marocchi via swift-evolution < swift-evolution@swift.org> wrote:

I remain very unconvinced that type casting should change code being
executed...

Sent from my iPhone

On 10 Feb 2017, at 21:04, David Waite via swift-evolution < swift-evolution@swift.org> wrote:

On Feb 10, 2017, at 12:41 PM, Robert Ryan via swift-evolution < swift-evolution@swift.org> wrote:

I’m hard pressed to think of a situation where you’d want the current Swift
3 behavior of the first example (where you can override a protocol default
implementation but you only want that override used when you reference the
class itself, but not when you reference the protocol as a type).

Thats easy - the extension methods are not related to protocol conformance.
The type implementing the protocol may not even know there *is* an
extension method, or that method could be added either later or by yet
other third party code.

As such, the type may not be overriding the extension method at all - the
protocol-implementing types may have their own method with a conflicting
name doing similar (or not) logic. It would not be appropriate for someone
calling the protocol extension method to get different behavior simply
because a type declared a similar named method.

In other words, without the method being declared on the protocol, override
behavior between an extension and type would be similar to unsafe “duck”
typing. There is no agreement by the type that they are conforming to
extension behavior, only to the protocol’s behavior.

IMHO the issue actually goes the other way
- there is no way to indicate that an extension method to a protocol is
meant to declare ‘default’ behavior, e.g. represents a partial
implementation for types conforming to the protocol.
- there is no way to indicate that a type property/method ‘implements’ the
functionality of a protocol.

The lack of a distinction between extensions declaring random methods and
declaring default implementation behavior is obviously an element of
confusion. Both points become an issue if the protocol method signatures
ever changes - the extension could no longer be providing a default
implementation, or a type may silently switch from their own implementation
to the default implementation provided by the extension.

-DW

If there are such examples, then add a “build setting” to allow you to turn
off this warning, or add some keyword to the declaration of the default
implementation that indicates that you’re allowing it to be overridden, but
protocol types won’t use it (e.g. nondynamic). Personally, I’d just add the
warning and call it a day (because I don’t know why you’d ever want the
current Swift 3 behavior).

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Rod Brown) #8

I agree this is a very... tricky part of Swift, and that many have stated it is expected behaviour.

I think this is fundamentally incorrect from a design standpoint with the concept of a protocol as an agreed interface, rather than an agreed implementation. By adding a Protocol Extensions, we have started to mix implementation with interface, which is understandable as those with the same interface often conform to the same behaviour.

That said, I think if my conforming to the protocol means I need to do extra work for my specific implementation case, then letting someone circumvent that by simply referring to me as the protocol is both dangerous and lets the protocol's extension implementation have mixed priority. My unique use case should be more important than the implementation that makes sense for the generic case.

My view is protocol extensions should a "default", nothing more. If you implement, you override. This behaviour preserves protocols as an agreed interface with additional default logic rather than a conflicting set of implementations.

I think I've stated this view before and been shot down, but I'm not sure.

Rod

···

On 11 Feb 2017, at 9:54 am, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

Robert, this is a tricky part of Swift. I raised the same point in my very first email to this list a year and a half ago, and the response was that no default/implement/override keyword was desired and that this behavior is intentional. It has since been raised multiple times on this list to the same conclusion.

The idea here is that protocol extension methods that are not protocol requirements are statically dispatched and can be shadowed but not overridden. If you read through some of the Swift-Evolution proposals, you'll see that there are several designs that actually seek to take advantage of this behavior. I can't recall which of these were actually implemented in that way, but it does indeed show that the behavior has its uses.

Many have suggested some sort of keyword to distinguish overriding and shadowing. However, every such proposed design thus far breaks retroactive conformance in some subtle way. It's hard to search the archives, but there are hundreds of emails on this topic.

On Fri, Feb 10, 2017 at 15:57 Goffredo Marocchi via swift-evolution <swift-evolution@swift.org> wrote:
I remain very unconvinced that type casting should change code being executed...

Sent from my iPhone

On 10 Feb 2017, at 21:04, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 10, 2017, at 12:41 PM, Robert Ryan via swift-evolution <swift-evolution@swift.org> wrote:

I’m hard pressed to think of a situation where you’d want the current Swift 3 behavior of the first example (where you can override a protocol default implementation but you only want that override used when you reference the class itself, but not when you reference the protocol as a type).

Thats easy - the extension methods are not related to protocol conformance. The type implementing the protocol may not even know there *is* an extension method, or that method could be added either later or by yet other third party code.

As such, the type may not be overriding the extension method at all - the protocol-implementing types may have their own method with a conflicting name doing similar (or not) logic. It would not be appropriate for someone calling the protocol extension method to get different behavior simply because a type declared a similar named method.

In other words, without the method being declared on the protocol, override behavior between an extension and type would be similar to unsafe “duck” typing. There is no agreement by the type that they are conforming to extension behavior, only to the protocol’s behavior.

IMHO the issue actually goes the other way
- there is no way to indicate that an extension method to a protocol is meant to declare ‘default’ behavior, e.g. represents a partial implementation for types conforming to the protocol.
- there is no way to indicate that a type property/method ‘implements’ the functionality of a protocol.

The lack of a distinction between extensions declaring random methods and declaring default implementation behavior is obviously an element of confusion. Both points become an issue if the protocol method signatures ever changes - the extension could no longer be providing a default implementation, or a type may silently switch from their own implementation to the default implementation provided by the extension.

-DW

If there are such examples, then add a “build setting” to allow you to turn off this warning, or add some keyword to the declaration of the default implementation that indicates that you’re allowing it to be overridden, but protocol types won’t use it (e.g. nondynamic). Personally, I’d just add the warning and call it a day (because I don’t know why you’d ever want the current Swift 3 behavior).

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #9

In a vacuum I'd have thought your idea to be superior in terms of
consistency and user-friendliness. In that world, protocols are strictly a
list of requirements and (optionally) defaults for those requirements.

In _this_ world, I think the idea is that protocols provide both behaviors
and customization points; not all customization points have default
behaviors, of course, but also not all behaviors are customizable defaults
(what we're discussing here). I can see how that's more sophisticated but
also more tricky. I don't see how such a model can be rolled back now that
we already have it, though.

···

On Fri, Feb 10, 2017 at 17:50 Rod Brown <rodney.brown6@icloud.com> wrote:

I agree this is a very... tricky part of Swift, and that many have stated
it is expected behaviour.

I think this is fundamentally incorrect from a design standpoint with the
concept of a protocol as an agreed interface, rather than an agreed
implementation. By adding a Protocol Extensions, we have started to mix
implementation with interface, which is understandable as those with the
same interface often conform to the same behaviour.

That said, I think if my conforming to the protocol means I need to do
extra work for my specific implementation case, then letting someone
circumvent that by simply referring to me as the protocol is both dangerous
and lets the protocol's extension implementation have mixed priority. My
unique use case should be more important than the implementation that makes
sense for the generic case.

My view is protocol extensions should a "default", nothing more. If you
implement, you override. This behaviour preserves protocols as an agreed
interface with additional default logic rather than a conflicting set of
implementations.

I think I've stated this view before and been shot down, but I'm not sure.

Rod

On 11 Feb 2017, at 9:54 am, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

Robert, this is a tricky part of Swift. I raised the same point in my very
first email to this list a year and a half ago, and the response was that
no default/implement/override keyword was desired and that this behavior is
intentional. It has since been raised multiple times on this list to the
same conclusion.

The idea here is that protocol extension methods that are not protocol
requirements are statically dispatched and can be shadowed but not
overridden. If you read through some of the Swift-Evolution proposals,
you'll see that there are several designs that actually seek to take
advantage of this behavior. I can't recall which of these were actually
implemented in that way, but it does indeed show that the behavior has its
uses.

Many have suggested some sort of keyword to distinguish overriding and
shadowing. However, every such proposed design thus far breaks retroactive
conformance in some subtle way. It's hard to search the archives, but there
are hundreds of emails on this topic.

On Fri, Feb 10, 2017 at 15:57 Goffredo Marocchi via swift-evolution < > swift-evolution@swift.org> wrote:

I remain very unconvinced that type casting should change code being
executed...

Sent from my iPhone

On 10 Feb 2017, at 21:04, David Waite via swift-evolution < > swift-evolution@swift.org> wrote:

On Feb 10, 2017, at 12:41 PM, Robert Ryan via swift-evolution < > swift-evolution@swift.org> wrote:

I’m hard pressed to think of a situation where you’d want the current
Swift 3 behavior of the first example (where you can override a protocol
default implementation but you only want that override used when you
reference the class itself, but not when you reference the protocol as a
type).

Thats easy - the extension methods are not related to protocol
conformance. The type implementing the protocol may not even know there
*is* an extension method, or that method could be added either later or by
yet other third party code.

As such, the type may not be overriding the extension method at all - the
protocol-implementing types may have their own method with a conflicting
name doing similar (or not) logic. It would not be appropriate for someone
calling the protocol extension method to get different behavior simply
because a type declared a similar named method.

In other words, without the method being declared on the protocol,
override behavior between an extension and type would be similar to unsafe
“duck” typing. There is no agreement by the type that they are conforming
to extension behavior, only to the protocol’s behavior.

IMHO the issue actually goes the other way
- there is no way to indicate that an extension method to a protocol is
meant to declare ‘default’ behavior, e.g. represents a partial
implementation for types conforming to the protocol.
- there is no way to indicate that a type property/method ‘implements’
the functionality of a protocol.

The lack of a distinction between extensions declaring random methods and
declaring default implementation behavior is obviously an element of
confusion. Both points become an issue if the protocol method signatures
ever changes - the extension could no longer be providing a default
implementation, or a type may silently switch from their own implementation
to the default implementation provided by the extension.

-DW

If there are such examples, then add a “build setting” to allow you to
turn off this warning, or add some keyword to the declaration of the
default implementation that indicates that you’re allowing it to be
overridden, but protocol types won’t use it (e.g. nondynamic).
Personally, I’d just add the warning and call it a day (because I don’t
know why you’d ever want the current Swift 3 behavior).

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Rod Brown) #10

I don't believe these two worlds are in conflict at all.

We currently have the default only state (without overrides), plus a conflict. We would simply be adding "override" functionality in part to clear the conflict.

With POP the idea is that the protocol, in knowing about how it is constituted, has a clear pattern for how it implements its behaviours. Therefore you can leverage the protocol to bear the brunt of the work, just as we do now.

The additional tweak to the design of protocols is to allow a user to define their own implementation where the default is not appropriate, or is incomplete for the use case. This doesn't work against POP - it simply observes that implementations at times may need to be customised to the use case, and allows that, as an optional override. It's clear people are trying to do this already because they already have overrides that are causing this conflict, and thus we are having the discussion.

In this case, I don't see overriding the protocol "default" as working against this world - I think it clarifies it.

Rod

···

On 11 Feb 2017, at 11:39 am, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

In a vacuum I'd have thought your idea to be superior in terms of consistency and user-friendliness. In that world, protocols are strictly a list of requirements and (optionally) defaults for those requirements.

In _this_ world, I think the idea is that protocols provide both behaviors and customization points; not all customization points have default behaviors, of course, but also not all behaviors are customizable defaults (what we're discussing here). I can see how that's more sophisticated but also more tricky. I don't see how such a model can be rolled back now that we already have it, though.

On Fri, Feb 10, 2017 at 17:50 Rod Brown <rodney.brown6@icloud.com> wrote:
I agree this is a very... tricky part of Swift, and that many have stated it is expected behaviour.

I think this is fundamentally incorrect from a design standpoint with the concept of a protocol as an agreed interface, rather than an agreed implementation. By adding a Protocol Extensions, we have started to mix implementation with interface, which is understandable as those with the same interface often conform to the same behaviour.

That said, I think if my conforming to the protocol means I need to do extra work for my specific implementation case, then letting someone circumvent that by simply referring to me as the protocol is both dangerous and lets the protocol's extension implementation have mixed priority. My unique use case should be more important than the implementation that makes sense for the generic case.

My view is protocol extensions should a "default", nothing more. If you implement, you override. This behaviour preserves protocols as an agreed interface with additional default logic rather than a conflicting set of implementations.

I think I've stated this view before and been shot down, but I'm not sure.

Rod

On 11 Feb 2017, at 9:54 am, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

Robert, this is a tricky part of Swift. I raised the same point in my very first email to this list a year and a half ago, and the response was that no default/implement/override keyword was desired and that this behavior is intentional. It has since been raised multiple times on this list to the same conclusion.

The idea here is that protocol extension methods that are not protocol requirements are statically dispatched and can be shadowed but not overridden. If you read through some of the Swift-Evolution proposals, you'll see that there are several designs that actually seek to take advantage of this behavior. I can't recall which of these were actually implemented in that way, but it does indeed show that the behavior has its uses.

Many have suggested some sort of keyword to distinguish overriding and shadowing. However, every such proposed design thus far breaks retroactive conformance in some subtle way. It's hard to search the archives, but there are hundreds of emails on this topic.

On Fri, Feb 10, 2017 at 15:57 Goffredo Marocchi via swift-evolution <swift-evolution@swift.org> wrote:
I remain very unconvinced that type casting should change code being executed...

Sent from my iPhone

On 10 Feb 2017, at 21:04, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

On Feb 10, 2017, at 12:41 PM, Robert Ryan via swift-evolution <swift-evolution@swift.org> wrote:

I’m hard pressed to think of a situation where you’d want the current Swift 3 behavior of the first example (where you can override a protocol default implementation but you only want that override used when you reference the class itself, but not when you reference the protocol as a type).

Thats easy - the extension methods are not related to protocol conformance. The type implementing the protocol may not even know there *is* an extension method, or that method could be added either later or by yet other third party code.

As such, the type may not be overriding the extension method at all - the protocol-implementing types may have their own method with a conflicting name doing similar (or not) logic. It would not be appropriate for someone calling the protocol extension method to get different behavior simply because a type declared a similar named method.

In other words, without the method being declared on the protocol, override behavior between an extension and type would be similar to unsafe “duck” typing. There is no agreement by the type that they are conforming to extension behavior, only to the protocol’s behavior.

IMHO the issue actually goes the other way
- there is no way to indicate that an extension method to a protocol is meant to declare ‘default’ behavior, e.g. represents a partial implementation for types conforming to the protocol.
- there is no way to indicate that a type property/method ‘implements’ the functionality of a protocol.

The lack of a distinction between extensions declaring random methods and declaring default implementation behavior is obviously an element of confusion. Both points become an issue if the protocol method signatures ever changes - the extension could no longer be providing a default implementation, or a type may silently switch from their own implementation to the default implementation provided by the extension.

-DW

If there are such examples, then add a “build setting” to allow you to turn off this warning, or add some keyword to the declaration of the default implementation that indicates that you’re allowing it to be overridden, but protocol types won’t use it (e.g. nondynamic). Personally, I’d just add the warning and call it a day (because I don’t know why you’d ever want the current Swift 3 behavior).

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #11

I don't believe these two worlds are in conflict at all.

We currently have the default only state (without overrides), plus a
conflict. We would simply be adding "override" functionality in part to
clear the conflict.

With POP the idea is that the protocol, in knowing about how it is
constituted, has a clear pattern for how it implements its behaviours.
Therefore you can leverage the protocol to bear the brunt of the work, just
as we do now.

The additional tweak to the design of protocols is to allow a user to
define their own implementation where the default is not appropriate, or is
incomplete for the use case.

I think I've written a poor explanation of my point. In Swift, it is the
library _author_, not the _user_, who gets the final say as to the upper
limits on what users can do with the author's types, by using modifiers
such as `final` and `open` (or the lack thereof). This has been the subject
of vehement opposition but, nonetheless, it is a clear and opinionated
decision on the part of the language. What you are critiquing as a bug is
regarded as a feature. That is to say, it is a way for the author of a
protocol extension method to deny to the user a customization point (in
other words, to disallow the outright overriding of the "default" behavior).

This doesn't work against POP - it simply observes that implementations at
times may need to be customised to the use case, and allows that, as an
optional override.

Again, the status quo in Swift is that it is up to the protocol's author to
determine which methods are overridable by the user and which are not. The
idea is that protocol extension methods that are not protocol requirements
are the intended way for disallowing such overriding.

As to its practical use: this guarantees that if protocol `P` has a method
`foo()`, it is possible to invoke `foo()` on an instance of existential
type `P` knowing that you will invoke the intended method. A concrete type
`T` that conforms to `P` may have its own `foo()` with totally different
semantics. After all, if `foo()` is not a protocol requirement, then
conforming types can have their own `foo()` do anything at all, with the
collision in name being mere coincidence and no guarantee of similar
semantics. And, with extensions, some third party can implement such a
`foo()` on `T` that the library author has no way of reasoning about. By
having a "shadowing" feature for `foo()`, I can know that no matter how
anyone in the future extends type `T`, I will invoke the intended `foo()`
with the right semantics on an instance of existential type `P`.

It's clear people are trying to do this already because they already have
overrides that are causing this conflict, and thus we are having the
discussion.

It sounds like what you are saying is that users of libraries are trying to
"override" protocol extension methods that authors of libraries have
designed not to be overridden. That this causes problems is, afaict, the
intended consequence of this feature and not an overlooked bug. For maximum
flexibility, however, Swift allows you to "shadow" the non-overridable
method with an identically named method of your own design that can have
different semantics.

···

On Fri, Feb 10, 2017 at 6:59 PM, Rod Brown <rodney.brown6@icloud.com> wrote:

In this case, I don't see overriding the protocol "default" as working
against this world - I think it clarifies it.

Rod

On 11 Feb 2017, at 11:39 am, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

In a vacuum I'd have thought your idea to be superior in terms of
consistency and user-friendliness. In that world, protocols are strictly a
list of requirements and (optionally) defaults for those requirements.

In _this_ world, I think the idea is that protocols provide both behaviors
and customization points; not all customization points have default
behaviors, of course, but also not all behaviors are customizable defaults
(what we're discussing here). I can see how that's more sophisticated but
also more tricky. I don't see how such a model can be rolled back now that
we already have it, though.
On Fri, Feb 10, 2017 at 17:50 Rod Brown <rodney.brown6@icloud.com> wrote:

I agree this is a very... tricky part of Swift, and that many have stated
it is expected behaviour.

I think this is fundamentally incorrect from a design standpoint with the
concept of a protocol as an agreed interface, rather than an agreed
implementation. By adding a Protocol Extensions, we have started to mix
implementation with interface, which is understandable as those with the
same interface often conform to the same behaviour.

That said, I think if my conforming to the protocol means I need to do
extra work for my specific implementation case, then letting someone
circumvent that by simply referring to me as the protocol is both dangerous
and lets the protocol's extension implementation have mixed priority. My
unique use case should be more important than the implementation that makes
sense for the generic case.

My view is protocol extensions should a "default", nothing more. If you
implement, you override. This behaviour preserves protocols as an agreed
interface with additional default logic rather than a conflicting set of
implementations.

I think I've stated this view before and been shot down, but I'm not sure.

Rod

On 11 Feb 2017, at 9:54 am, Xiaodi Wu via swift-evolution < >> swift-evolution@swift.org> wrote:

Robert, this is a tricky part of Swift. I raised the same point in my
very first email to this list a year and a half ago, and the response was
that no default/implement/override keyword was desired and that this
behavior is intentional. It has since been raised multiple times on this
list to the same conclusion.

The idea here is that protocol extension methods that are not protocol
requirements are statically dispatched and can be shadowed but not
overridden. If you read through some of the Swift-Evolution proposals,
you'll see that there are several designs that actually seek to take
advantage of this behavior. I can't recall which of these were actually
implemented in that way, but it does indeed show that the behavior has its
uses.

Many have suggested some sort of keyword to distinguish overriding and
shadowing. However, every such proposed design thus far breaks retroactive
conformance in some subtle way. It's hard to search the archives, but there
are hundreds of emails on this topic.

On Fri, Feb 10, 2017 at 15:57 Goffredo Marocchi via swift-evolution < >> swift-evolution@swift.org> wrote:

I remain very unconvinced that type casting should change code being
executed...

Sent from my iPhone

On 10 Feb 2017, at 21:04, David Waite via swift-evolution < >> swift-evolution@swift.org> wrote:

On Feb 10, 2017, at 12:41 PM, Robert Ryan via swift-evolution < >> swift-evolution@swift.org> wrote:

I’m hard pressed to think of a situation where you’d want the current
Swift 3 behavior of the first example (where you can override a protocol
default implementation but you only want that override used when you
reference the class itself, but not when you reference the protocol as a
type).

Thats easy - the extension methods are not related to protocol
conformance. The type implementing the protocol may not even know there
*is* an extension method, or that method could be added either later or by
yet other third party code.

As such, the type may not be overriding the extension method at all - the
protocol-implementing types may have their own method with a conflicting
name doing similar (or not) logic. It would not be appropriate for someone
calling the protocol extension method to get different behavior simply
because a type declared a similar named method.

In other words, without the method being declared on the protocol,
override behavior between an extension and type would be similar to unsafe
“duck” typing. There is no agreement by the type that they are conforming
to extension behavior, only to the protocol’s behavior.

IMHO the issue actually goes the other way
- there is no way to indicate that an extension method to a protocol is
meant to declare ‘default’ behavior, e.g. represents a partial
implementation for types conforming to the protocol.
- there is no way to indicate that a type property/method ‘implements’
the functionality of a protocol.

The lack of a distinction between extensions declaring random methods and
declaring default implementation behavior is obviously an element of
confusion. Both points become an issue if the protocol method signatures
ever changes - the extension could no longer be providing a default
implementation, or a type may silently switch from their own implementation
to the default implementation provided by the extension.

-DW

If there are such examples, then add a “build setting” to allow you to
turn off this warning, or add some keyword to the declaration of the
default implementation that indicates that you’re allowing it to be
overridden, but protocol types won’t use it (e.g. nondynamic).
Personally, I’d just add the warning and call it a day (because I don’t
know why you’d ever want the current Swift 3 behavior).

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Rod Brown) #12

I don't believe these two worlds are in conflict at all.

We currently have the default only state (without overrides), plus a conflict. We would simply be adding "override" functionality in part to clear the conflict.

With POP the idea is that the protocol, in knowing about how it is constituted, has a clear pattern for how it implements its behaviours. Therefore you can leverage the protocol to bear the brunt of the work, just as we do now.

The additional tweak to the design of protocols is to allow a user to define their own implementation where the default is not appropriate, or is incomplete for the use case.

I think I've written a poor explanation of my point. In Swift, it is the library _author_, not the _user_, who gets the final say as to the upper limits on what users can do with the author's types, by using modifiers such as `final` and `open` (or the lack thereof). This has been the subject of vehement opposition but, nonetheless, it is a clear and opinionated decision on the part of the language. What you are critiquing as a bug is regarded as a feature. That is to say, it is a way for the author of a protocol extension method to deny to the user a customization point (in other words, to disallow the outright overriding of the "default" behavior).

This doesn't work against POP - it simply observes that implementations at times may need to be customised to the use case, and allows that, as an optional override.

Again, the status quo in Swift is that it is up to the protocol's author to determine which methods are overridable by the user and which are not. The idea is that protocol extension methods that are not protocol requirements are the intended way for disallowing such overriding.

As to its practical use: this guarantees that if protocol `P` has a method `foo()`, it is possible to invoke `foo()` on an instance of existential type `P` knowing that you will invoke the intended method. A concrete type `T` that conforms to `P` may have its own `foo()` with totally different semantics. After all, if `foo()` is not a protocol requirement, then conforming types can have their own `foo()` do anything at all, with the collision in name being mere coincidence and no guarantee of similar semantics. And, with extensions, some third party can implement such a `foo()` on `T` that the library author has no way of reasoning about. By having a "shadowing" feature for `foo()`, I can know that no matter how anyone in the future extends type `T`, I will invoke the intended `foo()` with the right semantics on an instance of existential type `P`.

It's clear people are trying to do this already because they already have overrides that are causing this conflict, and thus we are having the discussion.

It sounds like what you are saying is that users of libraries are trying to "override" protocol extension methods that authors of libraries have designed not to be overridden. That this causes problems is, afaict, the intended consequence of this feature and not an overlooked bug. For maximum flexibility, however, Swift allows you to "shadow" the non-overridable method with an identically named method of your own design that can have different semantics.

If the intent is to restrict users and to provide the power to the author of the protocol, it appears not just opinionated, but arrogant.

As software developers, we need to work together to provide solutions to use cases. This includes not understanding what some use case may be. But instead, the consistent argument here has been that software should be as it is designed, to hell with the users of the frameworks who may have an unknown use case.

This therefore has created the shadow problem, where people implementing a protocol end up colliding with the same name, and must make casts back.

Why can't we make an adjustment and allow protocol implementers to define the cases where the protocol action is inappropriate.

I believe this argument stems back to the closed-by-default argument on classes with "public" and shows a concerning trend in how Swift is being developed. We cannot look at Swift purely from the perspective of the Standard Library, or one framework, or one implementer. It is how we coordinate between framework authors that we end up creating a unified system.

It should never be a feature to make users of frameworks fight with writers of frameworks. That's just absurd.

···

Sent from my iPhone

On 11 Feb 2017, at 2:20 pm, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Fri, Feb 10, 2017 at 6:59 PM, Rod Brown <rodney.brown6@icloud.com> wrote:

In this case, I don't see overriding the protocol "default" as working against this world - I think it clarifies it.

Rod


(Xiaodi Wu) #13

Sent from my iPhone

I don't believe these two worlds are in conflict at all.

We currently have the default only state (without overrides), plus a
conflict. We would simply be adding "override" functionality in part to
clear the conflict.

With POP the idea is that the protocol, in knowing about how it is
constituted, has a clear pattern for how it implements its behaviours.
Therefore you can leverage the protocol to bear the brunt of the work, just
as we do now.

The additional tweak to the design of protocols is to allow a user to
define their own implementation where the default is not appropriate, or is
incomplete for the use case.

I think I've written a poor explanation of my point. In Swift, it is the
library _author_, not the _user_, who gets the final say as to the upper
limits on what users can do with the author's types, by using modifiers
such as `final` and `open` (or the lack thereof). This has been the subject
of vehement opposition but, nonetheless, it is a clear and opinionated
decision on the part of the language. What you are critiquing as a bug is
regarded as a feature. That is to say, it is a way for the author of a
protocol extension method to deny to the user a customization point (in
other words, to disallow the outright overriding of the "default" behavior).

This doesn't work against POP - it simply observes that implementations
at times may need to be customised to the use case, and allows that, as an
optional override.

Again, the status quo in Swift is that it is up to the protocol's author
to determine which methods are overridable by the user and which are not.
The idea is that protocol extension methods that are not protocol
requirements are the intended way for disallowing such overriding.

As to its practical use: this guarantees that if protocol `P` has a method
`foo()`, it is possible to invoke `foo()` on an instance of existential
type `P` knowing that you will invoke the intended method. A concrete type
`T` that conforms to `P` may have its own `foo()` with totally different
semantics. After all, if `foo()` is not a protocol requirement, then
conforming types can have their own `foo()` do anything at all, with the
collision in name being mere coincidence and no guarantee of similar
semantics. And, with extensions, some third party can implement such a
`foo()` on `T` that the library author has no way of reasoning about. By
having a "shadowing" feature for `foo()`, I can know that no matter how
anyone in the future extends type `T`, I will invoke the intended `foo()`
with the right semantics on an instance of existential type `P`.

It's clear people are trying to do this already because they already have
overrides that are causing this conflict, and thus we are having the
discussion.

It sounds like what you are saying is that users of libraries are trying
to "override" protocol extension methods that authors of libraries have
designed not to be overridden. That this causes problems is, afaict, the
intended consequence of this feature and not an overlooked bug. For maximum
flexibility, however, Swift allows you to "shadow" the non-overridable
method with an identically named method of your own design that can have
different semantics.

If the intent is to restrict users and to provide the power to the author
of the protocol, it appears not just opinionated, but arrogant.

As software developers, we need to work together to provide solutions to
use cases. This includes not understanding what some use case may be. But
instead, the consistent argument here has been that software should be as
it is designed, to hell with the users of the frameworks who may have an
unknown use case.

This therefore has created the shadow problem, where people implementing a
protocol end up colliding with the same name, and must make casts back.

Why can't we make an adjustment and allow protocol implementers to define
the cases where the protocol action is inappropriate.

I believe this argument stems back to the closed-by-default argument on
classes with "public" and shows a concerning trend in how Swift is being
developed. We cannot look at Swift purely from the perspective of the
Standard Library, or one framework, or one implementer. It is how we
coordinate between framework authors that we end up creating a unified
system.

It should never be a feature to make users of frameworks fight with
writers of frameworks. That's just absurd.

That's an unfair characterization. Certainly, `public` vs `open` was hotly
debated, but the Swift core team came down on one side of that debate after
weighing all the arguments. As you say, this functionality of protocol
extension members is of a kind to that decision, and in fact it pre-dates
that decision.

Let's be clear that it's not about making users of libraries "fight" with
authors of libraries. In a language where a user can retroactively extend a
type, these facilities can be critical for allowing library authors to
reason about their own code. These guarantees aren't only for the original
author, either. See the currently ongoing thread about introducing a
`closed` keyword and how knowledge about restrictions on how a class, enum,
struct, or protocol can be changed by another party can help any user
reason about their own code. As you can see above, I offered an example
where even an end user can rely on `P.foo()` to do the same intended thing
(where `P` is a protocol) no matter how anyone else extends a conforming
type `T`. This is not at all absurd but a practical boon.

Can it be inconvenient if you happen to be the user of a type `P` and want
a customization point where the author has not designed one? Sure. But no
more so than if you happen to be the user of a type `P` and want to access
a private member where the author has decided not to make it public. If we
deem this all absurd we'd not have access modifiers either, and we'd
abandon all the work on the roadmap about library resilience, which can
also constrain library authors to allow more freedom for library users.

···

On Fri, Feb 10, 2017 at 9:54 PM, Rod Brown <rodney.brown6@icloud.com> wrote:

On 11 Feb 2017, at 2:20 pm, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:
On Fri, Feb 10, 2017 at 6:59 PM, Rod Brown <rodney.brown6@icloud.com> > wrote:

In this case, I don't see overriding the protocol "default" as working

against this world - I think it clarifies it.

Rod


(Rod Brown) #14

I see your point. I think the core issue here is customizability vs safety.

Allowing users to perform actions in these functions that change the behaviour of a protocol is potentially dangerous, and therefore Swift as a safe language disallows it.

This comes at the cost of being able to override the function dynamically and provide a different implementation that is perhaps more appropriate. This limits Swift flexibility.

Depending on where you come down on this, it may be "too conservative". Things can only be safe to a certain level and if we keep optimising for safety over functionality, perhaps we'll restrict Swift too much for it to be a useful language. Or perhaps it's a good trade. The decision regarding "closed by default" also had considerations like retrospectively "finalising" or "opening" APIs that made those decisions a little easier.

···

On 11 Feb 2017, at 3:32 pm, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Fri, Feb 10, 2017 at 9:54 PM, Rod Brown <rodney.brown6@icloud.com> wrote:

Sent from my iPhone

On 11 Feb 2017, at 2:20 pm, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Fri, Feb 10, 2017 at 6:59 PM, Rod Brown <rodney.brown6@icloud.com> wrote:
I don't believe these two worlds are in conflict at all.

We currently have the default only state (without overrides), plus a conflict. We would simply be adding "override" functionality in part to clear the conflict.

With POP the idea is that the protocol, in knowing about how it is constituted, has a clear pattern for how it implements its behaviours. Therefore you can leverage the protocol to bear the brunt of the work, just as we do now.

The additional tweak to the design of protocols is to allow a user to define their own implementation where the default is not appropriate, or is incomplete for the use case.

I think I've written a poor explanation of my point. In Swift, it is the library _author_, not the _user_, who gets the final say as to the upper limits on what users can do with the author's types, by using modifiers such as `final` and `open` (or the lack thereof). This has been the subject of vehement opposition but, nonetheless, it is a clear and opinionated decision on the part of the language. What you are critiquing as a bug is regarded as a feature. That is to say, it is a way for the author of a protocol extension method to deny to the user a customization point (in other words, to disallow the outright overriding of the "default" behavior).

This doesn't work against POP - it simply observes that implementations at times may need to be customised to the use case, and allows that, as an optional override.

Again, the status quo in Swift is that it is up to the protocol's author to determine which methods are overridable by the user and which are not. The idea is that protocol extension methods that are not protocol requirements are the intended way for disallowing such overriding.

As to its practical use: this guarantees that if protocol `P` has a method `foo()`, it is possible to invoke `foo()` on an instance of existential type `P` knowing that you will invoke the intended method. A concrete type `T` that conforms to `P` may have its own `foo()` with totally different semantics. After all, if `foo()` is not a protocol requirement, then conforming types can have their own `foo()` do anything at all, with the collision in name being mere coincidence and no guarantee of similar semantics. And, with extensions, some third party can implement such a `foo()` on `T` that the library author has no way of reasoning about. By having a "shadowing" feature for `foo()`, I can know that no matter how anyone in the future extends type `T`, I will invoke the intended `foo()` with the right semantics on an instance of existential type `P`.

It's clear people are trying to do this already because they already have overrides that are causing this conflict, and thus we are having the discussion.

It sounds like what you are saying is that users of libraries are trying to "override" protocol extension methods that authors of libraries have designed not to be overridden. That this causes problems is, afaict, the intended consequence of this feature and not an overlooked bug. For maximum flexibility, however, Swift allows you to "shadow" the non-overridable method with an identically named method of your own design that can have different semantics.

If the intent is to restrict users and to provide the power to the author of the protocol, it appears not just opinionated, but arrogant.

As software developers, we need to work together to provide solutions to use cases. This includes not understanding what some use case may be. But instead, the consistent argument here has been that software should be as it is designed, to hell with the users of the frameworks who may have an unknown use case.

This therefore has created the shadow problem, where people implementing a protocol end up colliding with the same name, and must make casts back.

Why can't we make an adjustment and allow protocol implementers to define the cases where the protocol action is inappropriate.

I believe this argument stems back to the closed-by-default argument on classes with "public" and shows a concerning trend in how Swift is being developed. We cannot look at Swift purely from the perspective of the Standard Library, or one framework, or one implementer. It is how we coordinate between framework authors that we end up creating a unified system.

It should never be a feature to make users of frameworks fight with writers of frameworks. That's just absurd.

That's an unfair characterization. Certainly, `public` vs `open` was hotly debated, but the Swift core team came down on one side of that debate after weighing all the arguments. As you say, this functionality of protocol extension members is of a kind to that decision, and in fact it pre-dates that decision.

Let's be clear that it's not about making users of libraries "fight" with authors of libraries. In a language where a user can retroactively extend a type, these facilities can be critical for allowing library authors to reason about their own code. These guarantees aren't only for the original author, either. See the currently ongoing thread about introducing a `closed` keyword and how knowledge about restrictions on how a class, enum, struct, or protocol can be changed by another party can help any user reason about their own code. As you can see above, I offered an example where even an end user can rely on `P.foo()` to do the same intended thing (where `P` is a protocol) no matter how anyone else extends a conforming type `T`. This is not at all absurd but a practical boon.

Can it be inconvenient if you happen to be the user of a type `P` and want a customization point where the author has not designed one? Sure. But no more so than if you happen to be the user of a type `P` and want to access a private member where the author has decided not to make it public. If we deem this all absurd we'd not have access modifiers either, and we'd abandon all the work on the roadmap about library resilience, which can also constrain library authors to allow more freedom for library users.

In this case, I don't see overriding the protocol "default" as working against this world - I think it clarifies it.

Rod