[Review] SE-0164: Remove final support in protocol extensions


(Douglas Gregor) #1

Hello Swift community,

The review of SE-0164 "Remove final support in protocol extensions" begins now and runs through April 8, 2017. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0164-remove-final-support-in-protocol-extensions.md
Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

https://lists.swift.org/mailman/listinfo/swift-evolution
or, if you would like to keep your feedback private, directly to the review manager. When replying, please try to keep the proposal link at the top of the message:

Proposal link:

https://github.com/apple/swift-evolution/blob/master/proposals/0164-remove-final-support-in-protocol-extensions.md
Reply text
Other replies
<https://github.com/apple/swift-evolution/pull/636#what-goes-into-a-review-1>What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

What is your evaluation of the proposal?
Is the problem being addressed significant enough to warrant a change to Swift?
Does this proposal fit well with the feel and direction of Swift?
If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
More information about the Swift evolution process is available at

https://github.com/apple/swift-evolution/blob/master/process.md
Thank you,

-Doug

Review Manager


(Lily Ballard) #2

It sounds like the keyword doesn't do anything right now, so +1.

-Kevin Ballard

···

On Wed, Apr 5, 2017, at 03:05 PM, Douglas Gregor wrote:

* What is your evaluation of the proposal?


(Howard Lovatt) #3

The review of SE-0164 "Remove final support in protocol extensions"

What is your evaluation of the proposal?

The present situation isn't great. People get confused about which method will called with protocol extensions. Seems like every week there is a variation on this confusion on Swift Users mailing list. Therefore something needs to be done.

However I am not keen on this proposal since it makes behaviour inconsistent between methods in protocol extensions, classes, and structs.

I think a better solution would be one of the following alternatives:

  1. Must use final and final means it cannot be overridden; or
  2. If not final dispatches using a table like a class and if marked final cannot be overridden and if marked dynamic uses obj-c dispatching; or
  3. Must be marked dynamic and uses obj-c dispatching.

My preference would be option 2 but I think any of the three is superior to the present situation or the proposal.

Is the problem being addressed significant enough to warrant a change to Swift?

Yes. Causes no end of confusion and is inconsistent.

Does this proposal fit well with the feel and direction of Swift?

Fixing the current behaviour yes, but not the proposal since it is inconsistent with the rest of the language.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Yes. Languages like Java and Scala use a method similar to option 2 above. But not something like the proposal.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Have suffered at the hands of the present behaviour when learning Swift.

-- Howard.

···

On 6 Apr 2017, at 8:05 am, Douglas Gregor <dgregor@apple.com> wrote:

Hello Swift community,

The review of SE-0164 "Remove final support in protocol extensions" begins now and runs through April 8, 2017. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0164-remove-final-support-in-protocol-extensions.md
Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

https://lists.swift.org/mailman/listinfo/swift-evolution
or, if you would like to keep your feedback private, directly to the review manager. When replying, please try to keep the proposal link at the top of the message:

Proposal link:

https://github.com/apple/swift-evolution/blob/master/proposals/0164-remove-final-support-in-protocol-extensions.md
Reply text
Other replies
What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

What is your evaluation of the proposal?
Is the problem being addressed significant enough to warrant a change to Swift?
Does this proposal fit well with the feel and direction of Swift?
If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
More information about the Swift evolution process is available at

https://github.com/apple/swift-evolution/blob/master/process.md
Thank you,

-Doug

Review Manager

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


(Jordan Rose) #4

[Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0164-remove-final-support-in-protocol-extensions.md]

+1 from me, for all the reasons in the proposal. 'final' doesn't mean anything in protocol extensions right now—it is literally ignored—so regardless of the actual justified confusion around extension members that satisfy requirements and those that don't, we should go forward with removing 'final'.

Jordan


(Víctor Pimentel) #5

   - What is your evaluation of the proposal?

+1

This small inconsistency can lead to pretty hard debugging session.

I think the protocols will still need some extra work because, as Howard
Lovatt said, people get confused about which method will be called when
methods of a type conflict with methods in a protocol extension.

But this is a good small step towards a more consistent model, so it seems
good to me.

   - Is the problem being addressed significant enough to warrant a
   change to Swift?

Yes

   - Does this proposal fit well with the feel and direction of Swift?

Yes

   - If you have used other languages or libraries with a similar
   feature, how do you feel that this proposal compares to those?

N/A

   - How much effort did you put into your review? A glance, a quick
   reading, or an in-depth study?

Quick reading.

···

On Thu, Apr 6, 2017 at 12:05 AM, Douglas Gregor via swift-evolution < swift-evolution@swift.org> wrote:

--
Víctor Pimentel


(Jordan Rose) #6

[Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0164-remove-final-support-in-protocol-extensions.md]

···

On Apr 5, 2017, at 16:15, Howard Lovatt via swift-evolution <swift-evolution@swift.org> wrote:

The review of SE-0164 "Remove final support in protocol extensions"

What is your evaluation of the proposal?

The present situation isn't great. People get confused about which method will called with protocol extensions. Seems like every week there is a variation on this confusion on Swift Users mailing list. Therefore something needs to be done.

However I am not keen on this proposal since it makes behaviour inconsistent between methods in protocol extensions, classes, and structs.

I think a better solution would be one of the following alternatives:

  1. Must use final and final means it cannot be overridden; or
  2. If not final dispatches using a table like a class and if marked final cannot be overridden and if marked dynamic uses obj-c dispatching; or
  3. Must be marked dynamic and uses obj-c dispatching.

My preference would be option 2 but I think any of the three is superior to the present situation or the proposal.

People have suggested all of these before, but none of them are obviously correct. It's true that we have a difference between extension members that satisfy requirements and those that don't, and that that confuses people. However, an extension-only member of one protocol can be used to satisfy the requirements of another protocol today, which is a tool for code reuse.

(I think we managed to convince everyone that it's just a bug that a protocol extension method that satisfies a requirement cannot be overridden in a subclass, so at least that isn't an issue on top of the rest of this.)

Oh, and we can't retroactively add members of a protocol extension to existing adopters, which is why protocol extension members cannot be @objc. There are limited circumstances where that would be safe, but that would be a separate proposal.

Jordan


(Hooman Mehr) #7

Agreed. +1

···

On Apr 6, 2017, at 11:51 AM, Jordan Rose via swift-evolution <swift-evolution@swift.org> wrote:

[Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0164-remove-final-support-in-protocol-extensions.md]

+1 from me, for all the reasons in the proposal. 'final' doesn't mean anything in protocol extensions right now—it is literally ignored—so regardless of the actual justified confusion around extension members that satisfy requirements and those that don't, we should go forward with removing 'final'.

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


(Howard Lovatt) #8

In response to Jordan Rose's comment I suggest the following change:

Proposal: Split extension usage up into implementing methods and adding static functions

Currently extension methods are confusing because they have different dispatch rules for the same syntax. EG:

    protocol P {
        func m()
    }
    extension P {
        func m() { print("P.m") }
    }
    struct S: P {
        func m() { print("S.m") }
    }
    val p: P = S() // Note typed as P
    p.m() // Surprisingly prints P.m even though S implements its own m
    val s = S() // Note typed as S
    s.m() // Prints S.m as expected

This proposal cures the above problem by separating extension methods into two seperate use cases: implementations for methods and adding static functions.

First implementing methods.

If the extension is in the same file as the protocol/struct/class declaration then it implements the methods and is dispatched using a Vtable. EG:

File P.swift
    protocol/struct/class P {
        func m()
    }
    extension P {
        func m() { print("P.m") }
    }

Same or other file
    struct S: P {
        override func m() { print("S.m") } // Note override required because m already has an implementation from the extension
    }
    val p: P = S() // Note typed as P
    p.m() // Now prints S.m as expected

Extensions in the same file as the declaration can have any access, can be final, and can have where clauses and provide inheritable implementations.

The implementation needed to achieve this is that a value instance typed as a protocol is copied onto the heap, a pointer to its Vtable added, and it is passed as a pointer. IE it becomes a class instance. No change needed for a class instance typed as a protocol.

The second use case is adding static functions.

A new type of extension is proposed, a static final extension, which can be either in or outside the file in which the protocol/struct/class declaration is in. EG:

    static final extension P { // Note extension marked static final
        func m() { print("P.m") }
    }

Which is called as any other static function would be called:

    val s = S()
    P.m(s) // Prints P.m as expected

The new static final extension is shorthand, particularly in the case of multiple functions, for:

    extension P {
        static final func m(_ this: P) { print("P.m") }
    }

If the static final extension is outside of the file in which the protocol/struct/class declaration is in then the extension and the methods can only have fileprivate and internal access.

As at present protocol/struct/class can have both a static and instance method of the same name, m in the case of the example, because the usage syntax is distinct. As at present, static final extensions, both the extension and the individual functions, can have where clauses.

In summary.

The proposal formalises the split use of extensions into their two uses: implementing methods and adding static functions. Syntax is added that clarifies both for declarations and usage which type of extension is provided/in use.

Note the distinction between an extension in the same file and in a separate file is consistent with the proposed use of private in https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md.

Comments?

-- Howard.

···

On 7 Apr 2017, at 4:49 am, Jordan Rose <jordan_rose@apple.com> wrote:

[Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0164-remove-final-support-in-protocol-extensions.md]

On Apr 5, 2017, at 16:15, Howard Lovatt via swift-evolution <swift-evolution@swift.org> wrote:

The review of SE-0164 "Remove final support in protocol extensions"

What is your evaluation of the proposal?

The present situation isn't great. People get confused about which method will called with protocol extensions. Seems like every week there is a variation on this confusion on Swift Users mailing list. Therefore something needs to be done.

However I am not keen on this proposal since it makes behaviour inconsistent between methods in protocol extensions, classes, and structs.

I think a better solution would be one of the following alternatives:

  1. Must use final and final means it cannot be overridden; or
  2. If not final dispatches using a table like a class and if marked final cannot be overridden and if marked dynamic uses obj-c dispatching; or
  3. Must be marked dynamic and uses obj-c dispatching.

My preference would be option 2 but I think any of the three is superior to the present situation or the proposal.

People have suggested all of these before, but none of them are obviously correct. It's true that we have a difference between extension members that satisfy requirements and those that don't, and that that confuses people. However, an extension-only member of one protocol can be used to satisfy the requirements of another protocol today, which is a tool for code reuse.

(I think we managed to convince everyone that it's just a bug that a protocol extension method that satisfies a requirement cannot be overridden in a subclass, so at least that isn't an issue on top of the rest of this.)

Oh, and we can't retroactively add members of a protocol extension to existing adopters, which is why protocol extension members cannot be @objc. There are limited circumstances where that would be safe, but that would be a separate proposal.

Jordan


(Xiaodi Wu) #9

In response to Jordan Rose's comment I suggest the following change:

Proposal: Split extension usage up into implementing methods and adding
static functions

Currently extension methods are confusing because they have different
dispatch rules for the same syntax. EG:

    protocol P {

        func m()

    }

    extension P {

        func m() { print("P.m") }

    }

    struct S: P {

        func m() { print("S.m") }

    }

    val p: P = S() // Note typed as P

    p.m() // Surprisingly prints P.m even though S implements its own m

This is incorrect. This prints "S.m", not "P.m".

    val s = S() // Note typed as S

    s.m() // Prints S.m as expected

This proposal cures the above problem by separating extension methods into
two seperate use cases: implementations for methods and adding static
functions.

First implementing methods.

If the extension is in the same file as the protocol/struct/class
declaration then it implements the methods and is dispatched using a
Vtable. EG:

File P.swift

    protocol/struct/class P {

        func m()

    }

    extension P {

        func m() { print("P.m") }

    }

Same or other file

    struct S: P {

        override func m() { print("S.m") } // Note override required
because m already has an implementation from the extension

Requiring `override` breaks retroactive conformance of types to protocols.
This idea has been brought up over half a dozen times. Each time it fails
in not being able to accommodate retroactive conformance.

    }

    val p: P = S() // Note typed as P

    p.m() // Now prints S.m as expected

Extensions in the same file as the declaration can have any access, can be
final, and can have where clauses and provide inheritable implementations.

The implementation needed to achieve this is that a value instance typed
as a protocol is copied onto the heap, a pointer to its Vtable added, and
it is passed as a pointer. IE it becomes a class instance. No change needed
for a class instance typed as a protocol.

The second use case is adding static functions.

A new type of extension is proposed, a static final extension, which can
be either in or outside the file in which the protocol/struct/class
declaration is in. EG:

    static final extension P { // Note extension marked static final

        func m() { print("P.m") }

    }

Which is called as any other static function would be called:

    val s = S()

    P.m(s) // Prints P.m as expected

The new static final extension is shorthand, particularly in the case of
multiple functions, for:

    extension P {

        static final func m(_ this: P) { print("P.m") }

    }

If the static final extension is outside of the file in which the
protocol/struct/class declaration is in then the extension and the methods
can only have fileprivate and internal access.

What is the use case for having this restriction? What is the problem you
are trying to solve?

···

On Mon, Apr 10, 2017 at 5:35 PM, Howard Lovatt via swift-evolution < swift-evolution@swift.org> wrote:

As at present protocol/struct/class can have both a static and instance
method of the same name, m in the case of the example, because the usage
syntax is distinct. As at present, static final extensions, both the
extension and the individual functions, can have where clauses.

In summary.

The proposal formalises the split use of extensions into their two uses:
implementing methods and adding static functions. Syntax is added that
clarifies both for declarations and usage which type of extension is
provided/in use.

Note the distinction between an extension in the same file and in a
separate file is consistent with the proposed use of private in
https://github.com/apple/swift-evolution/blob/master/
proposals/0169-improve-interaction-between-private-
declarations-and-extensions.md.

Comments?

-- Howard.

On 7 Apr 2017, at 4:49 am, Jordan Rose <jordan_rose@apple.com> wrote:

[Proposal: https://github.com/apple/swift-evolution/blob/
master/proposals/0164-remove-final-support-in-protocol-extensions.md]

On Apr 5, 2017, at 16:15, Howard Lovatt via swift-evolution < > swift-evolution@swift.org> wrote:

The review of SE-0164 "Remove final support in protocol extensions"

   - What is your evaluation of the proposal?

The present situation isn't great. People get confused about which method
will called with protocol extensions. Seems like every week there is a
variation on this confusion on Swift Users mailing list. Therefore
something needs to be done.

However I am not keen on this proposal since it makes behaviour
inconsistent between methods in protocol extensions, classes, and structs.

I think a better solution would be one of the following alternatives:

  1. Must use final and final means it cannot be overridden; or
  2. If not final dispatches using a table like a class and if marked
final cannot be overridden and if marked dynamic uses obj-c dispatching; or
  3. Must be marked dynamic and uses obj-c dispatching.

My preference would be option 2 but I think any of the three is superior
to the present situation or the proposal.

People have suggested all of these before, but none of them are obviously
correct. It's true that we have a difference between extension members that
satisfy requirements and those that don't, and that that confuses people.
However, an extension-only member of one protocol can be used to satisfy
the requirements of another protocol today, which is a tool for code reuse.

(I *think* we managed to convince everyone that it's just a bug that a
protocol extension method that satisfies a requirement cannot be overridden
in a subclass, so at least that isn't an issue on top of the rest of this.)

Oh, and we can't retroactively add members of a protocol extension to
existing adopters, which is why protocol extension members cannot be @objc.
There are limited circumstances where that would be safe, but that would be
a separate proposal.

Jordan

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


(Howard Lovatt) #10

@Xiaodi,

You make two drugs.

1. Deliberately making retroactive conformance outside of the file in which the type is declared illegal because of the problems it causes. See all the questions on Swift Users and watch people learning Swift get caught out.

2. Outside of the file in which the type is declared the static final extension is restricted to internal or fileprivate so that multiple modules can add static final extensions without clashes.

-- Howard.

···

On 11 Apr 2017, at 8:51 am, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Mon, Apr 10, 2017 at 5:35 PM, Howard Lovatt via swift-evolution <swift-evolution@swift.org> wrote:
In response to Jordan Rose's comment I suggest the following change:

Proposal: Split extension usage up into implementing methods and adding static functions

Currently extension methods are confusing because they have different dispatch rules for the same syntax. EG:

    protocol P {
        func m()
    }
    extension P {
        func m() { print("P.m") }
    }
    struct S: P {
        func m() { print("S.m") }
    }
    val p: P = S() // Note typed as P
    p.m() // Surprisingly prints P.m even though S implements its own m

This is incorrect. This prints "S.m", not "P.m".

    val s = S() // Note typed as S
    s.m() // Prints S.m as expected

This proposal cures the above problem by separating extension methods into two seperate use cases: implementations for methods and adding static functions.

First implementing methods.

If the extension is in the same file as the protocol/struct/class declaration then it implements the methods and is dispatched using a Vtable. EG:

File P.swift
    protocol/struct/class P {
        func m()
    }
    extension P {
        func m() { print("P.m") }
    }

Same or other file
    struct S: P {
        override func m() { print("S.m") } // Note override required because m already has an implementation from the extension

Requiring `override` breaks retroactive conformance of types to protocols. This idea has been brought up over half a dozen times. Each time it fails in not being able to accommodate retroactive conformance.

    }
    val p: P = S() // Note typed as P
    p.m() // Now prints S.m as expected

Extensions in the same file as the declaration can have any access, can be final, and can have where clauses and provide inheritable implementations.

The implementation needed to achieve this is that a value instance typed as a protocol is copied onto the heap, a pointer to its Vtable added, and it is passed as a pointer. IE it becomes a class instance. No change needed for a class instance typed as a protocol.

The second use case is adding static functions.

A new type of extension is proposed, a static final extension, which can be either in or outside the file in which the protocol/struct/class declaration is in. EG:

    static final extension P { // Note extension marked static final
        func m() { print("P.m") }
    }

Which is called as any other static function would be called:

    val s = S()
    P.m(s) // Prints P.m as expected

The new static final extension is shorthand, particularly in the case of multiple functions, for:

    extension P {
        static final func m(_ this: P) { print("P.m") }
    }

If the static final extension is outside of the file in which the protocol/struct/class declaration is in then the extension and the methods can only have fileprivate and internal access.

What is the use case for having this restriction? What is the problem you are trying to solve?

As at present protocol/struct/class can have both a static and instance method of the same name, m in the case of the example, because the usage syntax is distinct. As at present, static final extensions, both the extension and the individual functions, can have where clauses.

In summary.

The proposal formalises the split use of extensions into their two uses: implementing methods and adding static functions. Syntax is added that clarifies both for declarations and usage which type of extension is provided/in use.

Note the distinction between an extension in the same file and in a separate file is consistent with the proposed use of private in https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md.

Comments?

-- Howard.

On 7 Apr 2017, at 4:49 am, Jordan Rose <jordan_rose@apple.com> wrote:

[Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0164-remove-final-support-in-protocol-extensions.md]

On Apr 5, 2017, at 16:15, Howard Lovatt via swift-evolution <swift-evolution@swift.org> wrote:

The review of SE-0164 "Remove final support in protocol extensions"

What is your evaluation of the proposal?

The present situation isn't great. People get confused about which method will called with protocol extensions. Seems like every week there is a variation on this confusion on Swift Users mailing list. Therefore something needs to be done.

However I am not keen on this proposal since it makes behaviour inconsistent between methods in protocol extensions, classes, and structs.

I think a better solution would be one of the following alternatives:

  1. Must use final and final means it cannot be overridden; or
  2. If not final dispatches using a table like a class and if marked final cannot be overridden and if marked dynamic uses obj-c dispatching; or
  3. Must be marked dynamic and uses obj-c dispatching.

My preference would be option 2 but I think any of the three is superior to the present situation or the proposal.

People have suggested all of these before, but none of them are obviously correct. It's true that we have a difference between extension members that satisfy requirements and those that don't, and that that confuses people. However, an extension-only member of one protocol can be used to satisfy the requirements of another protocol today, which is a tool for code reuse.

(I think we managed to convince everyone that it's just a bug that a protocol extension method that satisfies a requirement cannot be overridden in a subclass, so at least that isn't an issue on top of the rest of this.)

Oh, and we can't retroactively add members of a protocol extension to existing adopters, which is why protocol extension members cannot be @objc. There are limited circumstances where that would be safe, but that would be a separate proposal.

Jordan

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


(Vladimir) #11

> In response to Jordan Rose's comment I suggest the following change:
>
> Proposal: Split extension usage up into implementing methods and adding
> static functions
>
> Currently extension methods are confusing because they have different
> dispatch rules for the same syntax. EG:
>
> protocol P {
>
> func m()
>
> }
>
> extension P {
>
> func m() { print("P.m") }
>
> }
>
> struct S: P {
>
> func m() { print("S.m") }
>
> }
>
> val p: P = S() // Note typed as P
>
> p.m() // Surprisingly prints P.m even though S implements its own m
>
> This is incorrect. This prints "S.m", not "P.m".

I believe the discussion was started is about protocol extensions methods that was not declared as protocol requirements:

protocol P {
  func foo()
}

struct S : P {
  func foo() {print("FOO from S")}
}

//----------------

extension P {
  func foo() {print("FOO default implementation from protocol")}
  
  // notice: bar was not declared as protocol requirement
  func bar() {print("BAR default implementation from protocol")}
}

extension S {
  func bar() {print("BAR from S")}
}

var p : P = S()
p.foo() // FOO from S
p.bar() // BAR default implementation from protocol

var s :S = S()
s.foo() // FOO from S
s.bar() // BAR from S

This is actually a big confusion point raised periodically in list and on stackoverflow etc. When you know how it works currently - you have no questions, but before this you can think that P.bar is same "thing" as P.foo. IMO Swift should help to clarify this situation with some kind of keyword/warning or actually make P.bar use the same rules as P.foo. After all, P.bar is a method in P protocol, S conforms to P protocol, so p.bar() logically should call S.bar() implementation.

At least IMO we need some marker for such protocol extension method that is not declared in protocol itself as requirement.

extension P {
  func foo() {...}
  
  func bar() {...} // Warning: 'bar' is not protocol requirement, use 'notrequirement' keyword
}

var p : P = S()
p.foo()
p.bar() // Warning: 'bar' is not-a-requirement method defined without 'notrequirement' keyword

So, to fix the warning:

extension P {
  func foo() {...}
  
  notrequirement func bar() {...}
}

('notrequirement' is not a proposal, just an example)

Personally I'd even require a "marker" on caller side, for example

p.bar() // Warning: 'bar' is not-a-requirement method, exact implementation of P.bar() will be called. Use explicit casting to silence the warning

(p as P).bar() // ok

>
> val s = S() // Note typed as S
>
> s.m() // Prints S.m as expected
>
> This proposal cures the above problem by separating extension methods
> into two seperate use cases: implementations for methods and adding
> static functions.
>
> First implementing methods.
>
> If the extension is in the same file as the protocol/struct/class
> declaration then it implements the methods and is dispatched using a
> Vtable. EG:
>
> File P.swift
>
> protocol/struct/class P {
>
> func m()
>
> }
>
> extension P {
>
> func m() { print("P.m") }
>
> }
>
> Same or other file
>
> struct S: P {
>
> override func m() { print("S.m") } // Note override required
> because m already has an implementation from the extension
>
> Requiring `override` breaks retroactive conformance of types to protocols.
> This idea has been brought up over half a dozen times. Each time it fails
> in not being able to accommodate retroactive conformance.

FWIW Actually, there were a number of suggestions where this point is solved with some additional keywords/syntax. But as I remember they also was not accepted.

IIRC something like this:

protocol P {
  func foo()
}

struct S : P {
  func foo() {} // no default implementation was known at the moment of
                       // *writing* this code
}

//another file

extension P {
  func foo() {} // this causes warning for S declaration "'override' is missed"
  func bar() {} // not-a-requrement
}

extension S {
  overriden foo() // silence the warning, notice 'overriden', no body
  
  override bar() {} // so S.bar should be called on S instance typed as P
}

Or this case:

struct S {
  func foo() {}
  func bar() {}
}

// another file

protocol P {
  func foo()
}

extension P {
  func foo() {}
  func bar() {} // not-a-requrement
}

// another file

extension S : P {
  overriden foo() // silence the warning
  overriden bar() // silence the warning
  
  // or even:
  //overriden P // instead of separate method names of P protocol
}

>
> }
>
> val p: P = S() // Note typed as P
>
> p.m() // Now prints S.m as expected
>
> Extensions in the same file as the declaration can have any access, can
> be final, and can have where clauses and provide inheritable
> implementations.
>
> The implementation needed to achieve this is that a value instance
> typed as a protocol is copied onto the heap, a pointer to its Vtable
> added, and it is passed as a pointer. IE it becomes a class instance.
> No change needed for a class instance typed as a protocol.
>
> The second use case is adding static functions.
>
> A new type of extension is proposed, a static final extension, which
> can be either in or outside the file in which the protocol/struct/class
> declaration is in. EG:
>
> static final extension P { // Note extension marked static final
>
> func m() { print("P.m") }
>
> }
>
> Which is called as any other static function would be called:
>
> val s = S()
>
> P.m(s) // Prints P.m as expected
>
> The new static final extension is shorthand, particularly in the case
> of multiple functions, for:
>
> extension P {
>
> static final func m(_ this: P) { print("P.m") }
>
> }
>
> If the static final extension is outside of the file in which the
> protocol/struct/class declaration is in then the extension and the
> methods can only have fileprivate and internal access.
>
> What is the use case for having this restriction? What is the problem you
> are trying to solve?
>
> As at present protocol/struct/class can have both a static and instance
> method of the same name, m in the case of the example, because the
> usage syntax is distinct. As at present, static final extensions, both
> the extension and the individual functions, can have where clauses.
>
> In summary.
>
> The proposal formalises the split use of extensions into their two
> uses: implementing methods and adding static functions. Syntax is added
> that clarifies both for declarations and usage which type of extension
> is provided/in use.
>
> Note the distinction between an extension in the same file and in a
> separate file is consistent with the proposed use of private in
> https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md
> <https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md>.
>
> Comments?
>
> -- Howard.
>
>> [Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0164-remove-final-support-in-protocol-extensions.md
>> <https://github.com/apple/swift-evolution/blob/master/proposals/0164-remove-final-support-in-protocol-extensions.md>]

···

On 11.04.2017 1:51, Xiaodi Wu via swift-evolution wrote:
> On Mon, Apr 10, 2017 at 5:35 PM, Howard Lovatt via swift-evolution > <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> On 7 Apr 2017, at 4:49 am, Jordan Rose <jordan_rose@apple.com > <mailto:jordan_rose@apple.com>> wrote:
>>
>>> On Apr 5, 2017, at 16:15, Howard Lovatt via swift-evolution >>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>>
>>> The review of SE-0164 "Remove final support in protocol extensions"
>>>
>>>> * What is your evaluation of the proposal?
>>> The present situation isn't great. People get confused about which
>>> method will called with protocol extensions. Seems like every week
>>> there is a variation on this confusion on Swift Users mailing list.
>>> Therefore something needs to be done.
>>>
>>> However I am not keen on this proposal since it makes behaviour
>>> inconsistent between methods in protocol extensions, classes, and
>>> structs.
>>>
>>> I think a better solution would be one of the following alternatives:
>>>
>>> 1. Must use final and final means it cannot be overridden; or
>>> 2. If not final dispatches using a table like a class and if
>>> marked final cannot be overridden and if marked dynamic uses obj-c
>>> dispatching; or
>>> 3. Must be marked dynamic and uses obj-c dispatching.
>>>
>>> My preference would be option 2 but I think any of the three is
>>> superior to the present situation or the proposal.
>>
>> People have suggested all of these before, but none of them are
>> obviously correct. It's true that we have a difference between
>> extension members that satisfy requirements and those that don't, and
>> that that confuses people. However, an extension-only member of one
>> protocol can be used to satisfy the requirements of another protocol
>> today, which is a tool for code reuse.
>>
>> (I /think/ we managed to convince everyone that it's just a bug that
>> a protocol extension method that satisfies a requirement cannot be
>> overridden in a subclass, so at least that isn't an issue on top of
>> the rest of this.)
>>
>> Oh, and we can't retroactively add members of a protocol extension to
>> existing adopters, which is why protocol extension members cannot be
>> @objc. There are limited circumstances where that would be safe, but
>> that would be a separate proposal.
>>
>> Jordan
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution
> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>


(Adrian Zubarev) #12

I myself pitched that requirement quite a few times on the list. Thank you, finally I understood why it’s not possible. Now I fully agree that the requirement of an override should not exist for the base type conforming to the protocol.

For those who might misunderstand the concept of retroactive conformances just like I did it, here is a quick snippet for you.

Imagine you’re working on some iOS project and for some reason you need an existential that only knows a property title of type String?.

protocol P { var title: String? { get set } }
Now you could retroactively conform types that already have this property to that protocol P without the need of implementing the property yourself. Think of it as a backdoor. :wink:

extension UIViewController : P {} // This just works
The proposed solution will simply break this nice feature.

···

--
Adrian Zubarev
Sent with Airmail

Am 11. April 2017 um 00:52:01, Xiaodi Wu via swift-evolution (swift-evolution@swift.org) schrieb:

Requiring `override` breaks retroactive conformance of types to protocols. This idea has been brought up over half a dozen times. Each time it fails in not being able to accommodate retroactive conformance.


(Xiaodi Wu) #13

As far as I'm aware, eliminating retroactive conformances is a non-starter.

···

On Mon, Apr 10, 2017 at 21:44 Howard Lovatt <howard.lovatt@gmail.com> wrote:

@Xiaodi,

You make two drugs.

1. Deliberately making retroactive conformance outside of the file in
which the type is declared illegal because of the problems it causes. See
all the questions on Swift Users and watch people learning Swift get caught
out.

2. Outside of the file in which the type is declared the static final
extension is restricted to internal or fileprivate so that multiple modules
can add static final extensions without clashes.

-- Howard.

On 11 Apr 2017, at 8:51 am, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Mon, Apr 10, 2017 at 5:35 PM, Howard Lovatt via swift-evolution < > swift-evolution@swift.org> wrote:

In response to Jordan Rose's comment I suggest the following change:

Proposal: Split extension usage up into implementing methods and adding
static functions

Currently extension methods are confusing because they have different
dispatch rules for the same syntax. EG:

    protocol P {

        func m()

    }

    extension P {

        func m() { print("P.m") }

    }

    struct S: P {

        func m() { print("S.m") }

    }

    val p: P = S() // Note typed as P

    p.m() // Surprisingly prints P.m even though S implements its own m

This is incorrect. This prints "S.m", not "P.m".

    val s = S() // Note typed as S

    s.m() // Prints S.m as expected

This proposal cures the above problem by separating extension methods into
two seperate use cases: implementations for methods and adding static
functions.

First implementing methods.

If the extension is in the same file as the protocol/struct/class
declaration then it implements the methods and is dispatched using a
Vtable. EG:

File P.swift

    protocol/struct/class P {

        func m()

    }

    extension P {

        func m() { print("P.m") }

    }

Same or other file

    struct S: P {

        override func m() { print("S.m") } // Note override required
because m already has an implementation from the extension

Requiring `override` breaks retroactive conformance of types to protocols.
This idea has been brought up over half a dozen times. Each time it fails
in not being able to accommodate retroactive conformance.

    }

    val p: P = S() // Note typed as P

    p.m() // Now prints S.m as expected

Extensions in the same file as the declaration can have any access, can be
final, and can have where clauses and provide inheritable implementations.

The implementation needed to achieve this is that a value instance typed
as a protocol is copied onto the heap, a pointer to its Vtable added, and
it is passed as a pointer. IE it becomes a class instance. No change needed
for a class instance typed as a protocol.

The second use case is adding static functions.

A new type of extension is proposed, a static final extension, which can
be either in or outside the file in which the protocol/struct/class
declaration is in. EG:

    static final extension P { // Note extension marked static final

        func m() { print("P.m") }

    }

Which is called as any other static function would be called:

    val s = S()

    P.m(s) // Prints P.m as expected

The new static final extension is shorthand, particularly in the case of
multiple functions, for:

    extension P {

        static final func m(_ this: P) { print("P.m") }

    }

If the static final extension is outside of the file in which the
protocol/struct/class declaration is in then the extension and the methods
can only have fileprivate and internal access.

What is the use case for having this restriction? What is the problem you
are trying to solve?

As at present protocol/struct/class can have both a static and instance
method of the same name, m in the case of the example, because the usage
syntax is distinct. As at present, static final extensions, both the
extension and the individual functions, can have where clauses.

In summary.

The proposal formalises the split use of extensions into their two uses:
implementing methods and adding static functions. Syntax is added that
clarifies both for declarations and usage which type of extension is
provided/in use.

Note the distinction between an extension in the same file and in a
separate file is consistent with the proposed use of private in
https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md
.

Comments?

-- Howard.

On 7 Apr 2017, at 4:49 am, Jordan Rose <jordan_rose@apple.com> wrote:

[Proposal:
https://github.com/apple/swift-evolution/blob/master/proposals/0164-remove-final-support-in-protocol-extensions.md
]

On Apr 5, 2017, at 16:15, Howard Lovatt via swift-evolution < > swift-evolution@swift.org> wrote:

The review of SE-0164 "Remove final support in protocol extensions"

   - What is your evaluation of the proposal?

The present situation isn't great. People get confused about which method
will called with protocol extensions. Seems like every week there is a
variation on this confusion on Swift Users mailing list. Therefore
something needs to be done.

However I am not keen on this proposal since it makes behaviour
inconsistent between methods in protocol extensions, classes, and structs.

I think a better solution would be one of the following alternatives:

  1. Must use final and final means it cannot be overridden; or
  2. If not final dispatches using a table like a class and if marked
final cannot be overridden and if marked dynamic uses obj-c dispatching; or
  3. Must be marked dynamic and uses obj-c dispatching.

My preference would be option 2 but I think any of the three is superior
to the present situation or the proposal.

People have suggested all of these before, but none of them are obviously
correct. It's true that we have a difference between extension members that
satisfy requirements and those that don't, and that that confuses people.
However, an extension-only member of one protocol can be used to satisfy
the requirements of another protocol today, which is a tool for code reuse.

(I *think* we managed to convince everyone that it's just a bug that a
protocol extension method that satisfies a requirement cannot be overridden
in a subclass, so at least that isn't an issue on top of the rest of this.)

Oh, and we can't retroactively add members of a protocol extension to
existing adopters, which is why protocol extension members cannot be @objc.
There are limited circumstances where that would be safe, but that would be
a separate proposal.

Jordan

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


(Jakub Suder) #14

Thanks for the explanation Adrian, I also had to Google this :slight_smile:

Personally I'd love some kind of solution that would prevent this confusion
of why this method does different things when called on the same object in
two different ways, but I don't have any ideas how this could be solved...
It's something that most people learning Swift definitely won't expect to
work as it works now (unless they're coming from a language that mostly
works with static dispatch, but I'm guessing most people won't).

···

On 11 April 2017 at 15:50, Adrian Zubarev via swift-evolution < swift-evolution@swift.org> wrote:

I myself pitched that requirement quite a few times on the list. Thank
you, finally I understood why it’s not possible. Now I fully agree that the
requirement of an override should not exist for the base type conforming to
the protocol.

For those who might misunderstand the concept of retroactive conformances
just like I did it, here is a quick snippet for you.

Imagine you’re working on some iOS project and for some reason you need an
existential that only knows a property title of type String?.

protocol P { var title: String? { get set } }

Now you could retroactively conform types that already have this property
to that protocol P without the need of implementing the property
yourself. Think of it as a backdoor. :wink:

extension UIViewController : P {} // This just works

The proposed solution will simply break this nice feature.

--
Adrian Zubarev
Sent with Airmail

Am 11. April 2017 um 00:52:01, Xiaodi Wu via swift-evolution (
swift-evolution@swift.org) schrieb:

Requiring `override` breaks retroactive conformance of types to protocols.
This idea has been brought up over half a dozen times. Each time it fails
in not being able to accommodate retroactive conformance.

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


(Howard Lovatt) #15

@Xiaodi,

You can safely post-hoc add protocols and methods provided that they are final, do not override, and are not exported from the module. See version 2 of the proposal below.

-- Howard.

# Proposal: Split extension usage up into implementing methods and adding methods and protocols post-hoc

Draft 2 (Added support for post-hoc conformance to a protocol - replaced static final extensions with final extensions)

## Introduction

Currently extension methods are confusing because they have different dispatch rules for the same calling syntax. EG:

    public protocol P {
        func mP() -> String
     }
    extension P {
        func mP() -> String { return "P.mP" }
        func mE() -> String { return "P.mE" }
    }
    struct S: P {
        func mP() -> String { return "S.mP" }
        func mE() -> String { return "S.mE" }
    }
    let s = S()
    s.mP() // S.mP as expected
    s.mE() // S.mE as expected
    let p: P = s // Note: s now typed as P
    p.mP() // S.mP as expected
    p.mE() // P.mE unexpected!

Extension methods can also cause compatibility problems between modules, consider:

In Module A
    extension Int: P {
        func m() -> String { print("A.m") }
    }

In Module B
    extension Int: P {
        func m() -> String { print("B.m") }
    }

In Module C
    import A
    import B // Should this be an error
    let i = 0
    i.m() // Should it return A.m or B.m?

This proposal cures the above two problems by separating extension methods into two seperate use cases: implementations for methods and adding methods and protocols post-hoc.

## Implementing methods

If the extension is in the same file as the protocol/struct/class declaration then it implements the methods and is dispatched using a Vtable. EG:

File P.swift
    protocol/struct/class P {
        // func m() not declared in type since it is added by the extension, under this proposal it is an error to include a declaration in a type *and* in an extension
    }
    extension P {
        func m() { print("P.m") } // m is added to the protocol/struct/class declaration
    }

Same or other file
    struct S: P {
        override func m() { print("S.m") } // Note override required because m already has an implementation from the extension
    }
    let p: P = S() // Note typed as P
    p.m() // Now prints S.m as expected

Extensions in the same file as the declaration can have any access, can be final, and can have where clauses and provide inheritable implementations.

In a protocol at present there is a difference in behaviour between a protocol that declares a method that is then implemented in an extension and a protocol that just has the method implemented in an extension and no declaration. This situation only applies to protocols, for structs and classes you cannot declare in type and implement in extensions. The proposal unifies the behaviour of protocol/struct/class with extensions and prevents the error of a minor typo between the protocol and extension adding two methods instead of generating an error.

The implementation needed to achieve this is that a value instance typed as a protocol is copied onto the heap, a pointer to its Vtable added, and it is passed as a pointer. IE it becomes a class instance. No change needed for a class instance typed as a protocol.

## Post-hoc adding protocols and methods

A new type of extension is proposed, a "final extension", which can be either in or outside the file in which the protocol/struct/class declaration is in. EG:

    protocol P2 {
        func m2P()
    }
    final extension S: P2 { // Note extension marked final
        func m2P() { print("SP2.m2P") } // Implicitly final, completely implements P2
        func m2E() { print("SP2.m2E") } // Implicitly final, not an existing method
    }

Which are called as any other method would be called:

    let s = S()
    s.m2P() // Prints SP2.m2P
    s.m2E() // Prints SP2.m2E

A method added by a final extension is implicitly final, as the name would suggest, and cannot be overridden.

If the final extension:

  1. Adds a method, e.g. m2E, that method cannot already exist. IE a final extension cannot override an existing method or implement a protocol declared method that lacks an implementation unless it also post-hoc adds the protocol.

  2. Adds a protocol then it must implement all the methods in that protocol that are not currently implemented.

  3. Is outside of the file in which the protocol/struct/class declaration is in then the extension and the methods can only have fileprivate or internal access. This prevents post-hoc extensions from numerous modules clashing, since they are not exported outside of the module.

## Possible future work (not part of this proposal)

This proposal will naturally allow bodies to be added to protocols directly rather than via an extension, since under the proposal the extension adds the declaration to the type so it is a small step to allow the protocol methods to have an implementation.

In an opposite sense to the above adding bodies to protocols, extensions could be allowed to add method declarations without bodies to protocols.

The two above future work proposals, if both added, would add symmetry to where declarations and bodies may appear for protocols.

## In summary.

The proposal formalises the split use of extensions into their two uses: implementing methods and post-hoc adding protocols and methods. Syntax is added that clarifies the two use cases, the former are termed extensions and must be in the same file as the type is declared, and the latter are termed final extensions and can be in any file, however if they are not in the type's file the they can only have fileprivate or internal access.

Note the distinction between an extension in the same file and in a separate file is consistent with the philosophy that there is special status to the same file as proposed for private in https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md.

···

On 11 Apr 2017, at 1:26 pm, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

As far as I'm aware, eliminating retroactive conformances is a non-starter.

On Mon, Apr 10, 2017 at 21:44 Howard Lovatt <howard.lovatt@gmail.com> wrote:
@Xiaodi,

You make two drugs.

1. Deliberately making retroactive conformance outside of the file in which the type is declared illegal because of the problems it causes. See all the questions on Swift Users and watch people learning Swift get caught out.

2. Outside of the file in which the type is declared the static final extension is restricted to internal or fileprivate so that multiple modules can add static final extensions without clashes.

-- Howard.

On 11 Apr 2017, at 8:51 am, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Mon, Apr 10, 2017 at 5:35 PM, Howard Lovatt via swift-evolution <swift-evolution@swift.org> wrote:
In response to Jordan Rose's comment I suggest the following change:

Proposal: Split extension usage up into implementing methods and adding static functions

Currently extension methods are confusing because they have different dispatch rules for the same syntax. EG:

    protocol P {
        func m()
    }
    extension P {
        func m() { print("P.m") }
    }
    struct S: P {
        func m() { print("S.m") }
    }
    val p: P = S() // Note typed as P
    p.m() // Surprisingly prints P.m even though S implements its own m

This is incorrect. This prints "S.m", not "P.m".

    val s = S() // Note typed as S
    s.m() // Prints S.m as expected

This proposal cures the above problem by separating extension methods into two seperate use cases: implementations for methods and adding static functions.

First implementing methods.

If the extension is in the same file as the protocol/struct/class declaration then it implements the methods and is dispatched using a Vtable. EG:

File P.swift
    protocol/struct/class P {
        func m()
    }
    extension P {
        func m() { print("P.m") }
    }

Same or other file
    struct S: P {
        override func m() { print("S.m") } // Note override required because m already has an implementation from the extension

Requiring `override` breaks retroactive conformance of types to protocols. This idea has been brought up over half a dozen times. Each time it fails in not being able to accommodate retroactive conformance.

    }
    val p: P = S() // Note typed as P
    p.m() // Now prints S.m as expected

Extensions in the same file as the declaration can have any access, can be final, and can have where clauses and provide inheritable implementations.

The implementation needed to achieve this is that a value instance typed as a protocol is copied onto the heap, a pointer to its Vtable added, and it is passed as a pointer. IE it becomes a class instance. No change needed for a class instance typed as a protocol.

The second use case is adding static functions.

A new type of extension is proposed, a static final extension, which can be either in or outside the file in which the protocol/struct/class declaration is in. EG:

    static final extension P { // Note extension marked static final
        func m() { print("P.m") }
    }

Which is called as any other static function would be called:

    val s = S()
    P.m(s) // Prints P.m as expected

The new static final extension is shorthand, particularly in the case of multiple functions, for:

    extension P {
        static final func m(_ this: P) { print("P.m") }
    }

If the static final extension is outside of the file in which the protocol/struct/class declaration is in then the extension and the methods can only have fileprivate and internal access.

What is the use case for having this restriction? What is the problem you are trying to solve?

As at present protocol/struct/class can have both a static and instance method of the same name, m in the case of the example, because the usage syntax is distinct. As at present, static final extensions, both the extension and the individual functions, can have where clauses.

In summary.

The proposal formalises the split use of extensions into their two uses: implementing methods and adding static functions. Syntax is added that clarifies both for declarations and usage which type of extension is provided/in use.

Note the distinction between an extension in the same file and in a separate file is consistent with the proposed use of private in https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md.

Comments?

-- Howard.

On 7 Apr 2017, at 4:49 am, Jordan Rose <jordan_rose@apple.com> wrote:

[Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0164-remove-final-support-in-protocol-extensions.md]

On Apr 5, 2017, at 16:15, Howard Lovatt via swift-evolution <swift-evolution@swift.org> wrote:

The review of SE-0164 "Remove final support in protocol extensions"

What is your evaluation of the proposal?

The present situation isn't great. People get confused about which method will called with protocol extensions. Seems like every week there is a variation on this confusion on Swift Users mailing list. Therefore something needs to be done.

However I am not keen on this proposal since it makes behaviour inconsistent between methods in protocol extensions, classes, and structs.

I think a better solution would be one of the following alternatives:

  1. Must use final and final means it cannot be overridden; or
  2. If not final dispatches using a table like a class and if marked final cannot be overridden and if marked dynamic uses obj-c dispatching; or
  3. Must be marked dynamic and uses obj-c dispatching.

My preference would be option 2 but I think any of the three is superior to the present situation or the proposal.

People have suggested all of these before, but none of them are obviously correct. It's true that we have a difference between extension members that satisfy requirements and those that don't, and that that confuses people. However, an extension-only member of one protocol can be used to satisfy the requirements of another protocol today, which is a tool for code reuse.

(I think we managed to convince everyone that it's just a bug that a protocol extension method that satisfies a requirement cannot be overridden in a subclass, so at least that isn't an issue on top of the rest of this.)

Oh, and we can't retroactively add members of a protocol extension to existing adopters, which is why protocol extension members cannot be @objc. There are limited circumstances where that would be safe, but that would be a separate proposal.

Jordan

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


(Xiaodi Wu) #16

The retroactive conformance needs to be exportable. If one cannot vend a
library that conforms standard library types to new protocols, then that is
a non-starter.

···

On Thu, Apr 13, 2017 at 06:07 Howard Lovatt <howard.lovatt@gmail.com> wrote:

@Xiaodi,

You can safely post-hoc add protocols and methods provided that they are
final, do not override, and are not exported from the module. See version 2
of the proposal below.

-- Howard.

# Proposal: Split extension usage up into implementing methods and adding
methods and protocols post-hoc

Draft 2 (Added support for post-hoc conformance to a protocol - replaced
static final extensions with final extensions)

## Introduction

Currently extension methods are confusing because they have different
dispatch rules for the same calling syntax. EG:

    public protocol P {

        func mP() -> String

     }

    extension P {

        func mP() -> String { return "P.mP" }

        func mE() -> String { return "P.mE" }

    }

    struct S: P {

        func mP() -> String { return "S.mP" }

        func mE() -> String { return "S.mE" }

    }

    let s = S()

    s.mP() // S.mP as expected

    s.mE() // S.mE as expected

    let p: P = s // Note: s now typed as P

    p.mP() // S.mP as expected

    p.mE() // P.mE unexpected!

Extension methods can also cause compatibility problems between modules,
consider:

In Module A

    extension Int: P {

        func m() -> String { print("A.m") }

    }

In Module B

    extension Int: P {

        func m() -> String { print("B.m") }

    }

In Module C

    import A

    import B // Should this be an error

    let i = 0

    i.m() // Should it return A.m or B.m?

This proposal cures the above two problems by separating extension methods
into two seperate use cases: implementations for methods and adding methods
and protocols post-hoc.

## Implementing methods

If the extension is in the same file as the protocol/struct/class
declaration then it implements the methods and is dispatched using a
Vtable. EG:

File P.swift

    protocol/struct/class P {

        // func m() not declared in type since it is added by the
extension, under this proposal it is an error to include a declaration in a
type *and* in an extension

    }

    extension P {

        func m() { print("P.m") } // m is added to the
protocol/struct/class declaration

    }

Same or other file

    struct S: P {

        override func m() { print("S.m") } // Note override required
because m already has an implementation from the extension

    }

    let p: P = S() // Note typed as P

    p.m() // Now prints S.m as expected

Extensions in the same file as the declaration can have any access, can be
final, and can have where clauses and provide inheritable implementations.

In a protocol at present there is a difference in behaviour between a
protocol that declares a method that is then implemented in an extension
and a protocol that just has the method implemented in an extension and no
declaration. This situation only applies to protocols, for structs and
classes you cannot declare in type and implement in extensions. The
proposal unifies the behaviour of protocol/struct/class with extensions and
prevents the error of a minor typo between the protocol and extension
adding two methods instead of generating an error.

The implementation needed to achieve this is that a value instance typed
as a protocol is copied onto the heap, a pointer to its Vtable added, and
it is passed as a pointer. IE it becomes a class instance. No change needed
for a class instance typed as a protocol.

## Post-hoc adding protocols and methods

A new type of extension is proposed, a "final extension", which can be
either in or outside the file in which the protocol/struct/class
declaration is in. EG:

    protocol P2 {

        func m2P()

    }

    final extension S: P2 { // Note extension marked final

        func m2P() { print("SP2.m2P") } // Implicitly final, completely
implements P2

        func m2E() { print("SP2.m2E") } // Implicitly final, not an
existing method

    }

Which are called as any other method would be called:

    let s = S()

    s.m2P() // Prints SP2.m2P

    s.m2E() // Prints SP2.m2E

A method added by a final extension is implicitly final, as the name would
suggest, and cannot be overridden.

If the final extension:

  1. Adds a method, e.g. m2E, that method cannot already exist. IE a final
extension cannot override an existing method or implement a protocol
declared method that lacks an implementation unless it also post-hoc adds
the protocol.

  2. Adds a protocol then it must implement all the methods in that
protocol that are not currently implemented.

  3. Is outside of the file in which the protocol/struct/class declaration
is in then the extension and the methods can only have fileprivate or
internal access. This prevents post-hoc extensions from numerous modules
clashing, since they are not exported outside of the module.

## Possible future work (not part of this proposal)

This proposal will naturally allow bodies to be added to protocols
directly rather than via an extension, since under the proposal the
extension adds the declaration to the type so it is a small step to allow
the protocol methods to have an implementation.

In an opposite sense to the above adding bodies to protocols, extensions
could be allowed to add method declarations without bodies to protocols.

The two above future work proposals, if both added, would add symmetry to
where declarations and bodies may appear for protocols.

## In summary.

The proposal formalises the split use of extensions into their two uses:
implementing methods and post-hoc adding protocols and methods. Syntax is
added that clarifies the two use cases, the former are termed extensions
and must be in the same file as the type is declared, and the latter are
termed final extensions and can be in any file, however if they are not in
the type's file the they can only have fileprivate or internal access.

Note the distinction between an extension in the same file and in a
separate file is consistent with the philosophy that there is special
status to the same file as proposed for private in
https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md
.

On 11 Apr 2017, at 1:26 pm, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

As far as I'm aware, eliminating retroactive conformances is a non-starter.

On Mon, Apr 10, 2017 at 21:44 Howard Lovatt <howard.lovatt@gmail.com> > wrote:

@Xiaodi,

You make two drugs.

1. Deliberately making retroactive conformance outside of the file in
which the type is declared illegal because of the problems it causes. See
all the questions on Swift Users and watch people learning Swift get caught
out.

2. Outside of the file in which the type is declared the static final
extension is restricted to internal or fileprivate so that multiple modules
can add static final extensions without clashes.

-- Howard.

On 11 Apr 2017, at 8:51 am, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Mon, Apr 10, 2017 at 5:35 PM, Howard Lovatt via swift-evolution < >> swift-evolution@swift.org> wrote:

In response to Jordan Rose's comment I suggest the following change:

Proposal: Split extension usage up into implementing methods and adding
static functions

Currently extension methods are confusing because they have different
dispatch rules for the same syntax. EG:

    protocol P {

        func m()

    }

    extension P {

        func m() { print("P.m") }

    }

    struct S: P {

        func m() { print("S.m") }

    }

    val p: P = S() // Note typed as P

    p.m() // Surprisingly prints P.m even though S implements its own m

This is incorrect. This prints "S.m", not "P.m".

    val s = S() // Note typed as S

    s.m() // Prints S.m as expected

This proposal cures the above problem by separating extension methods
into two seperate use cases: implementations for methods and adding static
functions.

First implementing methods.

If the extension is in the same file as the protocol/struct/class
declaration then it implements the methods and is dispatched using a
Vtable. EG:

File P.swift

    protocol/struct/class P {

        func m()

    }

    extension P {

        func m() { print("P.m") }

    }

Same or other file

    struct S: P {

        override func m() { print("S.m") } // Note override required
because m already has an implementation from the extension

Requiring `override` breaks retroactive conformance of types to
protocols. This idea has been brought up over half a dozen times. Each time
it fails in not being able to accommodate retroactive conformance.

    }

    val p: P = S() // Note typed as P

    p.m() // Now prints S.m as expected

Extensions in the same file as the declaration can have any access, can
be final, and can have where clauses and provide inheritable
implementations.

The implementation needed to achieve this is that a value instance typed
as a protocol is copied onto the heap, a pointer to its Vtable added, and
it is passed as a pointer. IE it becomes a class instance. No change needed
for a class instance typed as a protocol.

The second use case is adding static functions.

A new type of extension is proposed, a static final extension, which can
be either in or outside the file in which the protocol/struct/class
declaration is in. EG:

    static final extension P { // Note extension marked static final

        func m() { print("P.m") }

    }

Which is called as any other static function would be called:

    val s = S()

    P.m(s) // Prints P.m as expected

The new static final extension is shorthand, particularly in the case of
multiple functions, for:

    extension P {

        static final func m(_ this: P) { print("P.m") }

    }

If the static final extension is outside of the file in which the
protocol/struct/class declaration is in then the extension and the methods
can only have fileprivate and internal access.

What is the use case for having this restriction? What is the problem you
are trying to solve?

As at present protocol/struct/class can have both a static and instance
method of the same name, m in the case of the example, because the usage
syntax is distinct. As at present, static final extensions, both the
extension and the individual functions, can have where clauses.

In summary.

The proposal formalises the split use of extensions into their two uses:
implementing methods and adding static functions. Syntax is added that
clarifies both for declarations and usage which type of extension is
provided/in use.

Note the distinction between an extension in the same file and in a
separate file is consistent with the proposed use of private in
https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md
.

Comments?

-- Howard.

On 7 Apr 2017, at 4:49 am, Jordan Rose <jordan_rose@apple.com> wrote:

[Proposal:
https://github.com/apple/swift-evolution/blob/master/proposals/0164-remove-final-support-in-protocol-extensions.md
]

On Apr 5, 2017, at 16:15, Howard Lovatt via swift-evolution < >> swift-evolution@swift.org> wrote:

The review of SE-0164 "Remove final support in protocol extensions"

   - What is your evaluation of the proposal?

The present situation isn't great. People get confused about which method
will called with protocol extensions. Seems like every week there is a
variation on this confusion on Swift Users mailing list. Therefore
something needs to be done.

However I am not keen on this proposal since it makes behaviour
inconsistent between methods in protocol extensions, classes, and structs.

I think a better solution would be one of the following alternatives:

  1. Must use final and final means it cannot be overridden; or
  2. If not final dispatches using a table like a class and if marked
final cannot be overridden and if marked dynamic uses obj-c dispatching; or
  3. Must be marked dynamic and uses obj-c dispatching.

My preference would be option 2 but I think any of the three is superior
to the present situation or the proposal.

People have suggested all of these before, but none of them are obviously
correct. It's true that we have a difference between extension members that
satisfy requirements and those that don't, and that that confuses people.
However, an extension-only member of one protocol can be used to satisfy
the requirements of another protocol today, which is a tool for code reuse.

(I *think* we managed to convince everyone that it's just a bug that a
protocol extension method that satisfies a requirement cannot be overridden
in a subclass, so at least that isn't an issue on top of the rest of this.)

Oh, and we can't retroactively add members of a protocol extension to
existing adopters, which is why protocol extension members cannot be @objc.
There are limited circumstances where that would be safe, but that would be
a separate proposal.

Jordan

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


(Howard Lovatt) #17

I don't see that retroactive conformance needs to be exportable. If it is exported then you cannot prevent clashes from two modules, this is a known problem in C#. Because of this and other problems with C# extensions, this style of extension were rejected by other language communities (notably Java and Scala).

A better alternative for export is a new class that encapsulates the standard type but with added methods for the protocol to be added. This way there is no clash between modules. EG:

    public protocol P {
        func m() -> String
    }
    public class PInt: P {
        var value = 0
        func m() -> String { return "PI.m" }
    }

-- Howard.

···

On 13 Apr 2017, at 10:31 pm, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

The retroactive conformance needs to be exportable. If one cannot vend a library that conforms standard library types to new protocols, then that is a non-starter.

On Thu, Apr 13, 2017 at 06:07 Howard Lovatt <howard.lovatt@gmail.com> wrote:
@Xiaodi,

You can safely post-hoc add protocols and methods provided that they are final, do not override, and are not exported from the module. See version 2 of the proposal below.

-- Howard.

# Proposal: Split extension usage up into implementing methods and adding methods and protocols post-hoc

Draft 2 (Added support for post-hoc conformance to a protocol - replaced static final extensions with final extensions)

## Introduction

Currently extension methods are confusing because they have different dispatch rules for the same calling syntax. EG:

    public protocol P {
        func mP() -> String
     }
    extension P {
        func mP() -> String { return "P.mP" }
        func mE() -> String { return "P.mE" }
    }
    struct S: P {
        func mP() -> String { return "S.mP" }
        func mE() -> String { return "S.mE" }
    }
    let s = S()
    s.mP() // S.mP as expected
    s.mE() // S.mE as expected
    let p: P = s // Note: s now typed as P
    p.mP() // S.mP as expected
    p.mE() // P.mE unexpected!

Extension methods can also cause compatibility problems between modules, consider:

In Module A
    extension Int: P {
        func m() -> String { print("A.m") }
    }

In Module B
    extension Int: P {
        func m() -> String { print("B.m") }
    }

In Module C
    import A
    import B // Should this be an error
    let i = 0
    i.m() // Should it return A.m or B.m?

This proposal cures the above two problems by separating extension methods into two seperate use cases: implementations for methods and adding methods and protocols post-hoc.

## Implementing methods

If the extension is in the same file as the protocol/struct/class declaration then it implements the methods and is dispatched using a Vtable. EG:

File P.swift
    protocol/struct/class P {
        // func m() not declared in type since it is added by the extension, under this proposal it is an error to include a declaration in a type *and* in an extension
    }
    extension P {
        func m() { print("P.m") } // m is added to the protocol/struct/class declaration
    }

Same or other file
    struct S: P {
        override func m() { print("S.m") } // Note override required because m already has an implementation from the extension
    }
    let p: P = S() // Note typed as P
    p.m() // Now prints S.m as expected

Extensions in the same file as the declaration can have any access, can be final, and can have where clauses and provide inheritable implementations.

In a protocol at present there is a difference in behaviour between a protocol that declares a method that is then implemented in an extension and a protocol that just has the method implemented in an extension and no declaration. This situation only applies to protocols, for structs and classes you cannot declare in type and implement in extensions. The proposal unifies the behaviour of protocol/struct/class with extensions and prevents the error of a minor typo between the protocol and extension adding two methods instead of generating an error.

The implementation needed to achieve this is that a value instance typed as a protocol is copied onto the heap, a pointer to its Vtable added, and it is passed as a pointer. IE it becomes a class instance. No change needed for a class instance typed as a protocol.

## Post-hoc adding protocols and methods

A new type of extension is proposed, a "final extension", which can be either in or outside the file in which the protocol/struct/class declaration is in. EG:

    protocol P2 {
        func m2P()
    }
    final extension S: P2 { // Note extension marked final
        func m2P() { print("SP2.m2P") } // Implicitly final, completely implements P2
        func m2E() { print("SP2.m2E") } // Implicitly final, not an existing method
    }

Which are called as any other method would be called:

    let s = S()
    s.m2P() // Prints SP2.m2P
    s.m2E() // Prints SP2.m2E

A method added by a final extension is implicitly final, as the name would suggest, and cannot be overridden.

If the final extension:

  1. Adds a method, e.g. m2E, that method cannot already exist. IE a final extension cannot override an existing method or implement a protocol declared method that lacks an implementation unless it also post-hoc adds the protocol.

  2. Adds a protocol then it must implement all the methods in that protocol that are not currently implemented.

  3. Is outside of the file in which the protocol/struct/class declaration is in then the extension and the methods can only have fileprivate or internal access. This prevents post-hoc extensions from numerous modules clashing, since they are not exported outside of the module.

## Possible future work (not part of this proposal)

This proposal will naturally allow bodies to be added to protocols directly rather than via an extension, since under the proposal the extension adds the declaration to the type so it is a small step to allow the protocol methods to have an implementation.

In an opposite sense to the above adding bodies to protocols, extensions could be allowed to add method declarations without bodies to protocols.

The two above future work proposals, if both added, would add symmetry to where declarations and bodies may appear for protocols.

## In summary.

The proposal formalises the split use of extensions into their two uses: implementing methods and post-hoc adding protocols and methods. Syntax is added that clarifies the two use cases, the former are termed extensions and must be in the same file as the type is declared, and the latter are termed final extensions and can be in any file, however if they are not in the type's file the they can only have fileprivate or internal access.

Note the distinction between an extension in the same file and in a separate file is consistent with the philosophy that there is special status to the same file as proposed for private in https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md.

On 11 Apr 2017, at 1:26 pm, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

As far as I'm aware, eliminating retroactive conformances is a non-starter.

On Mon, Apr 10, 2017 at 21:44 Howard Lovatt <howard.lovatt@gmail.com> wrote:
@Xiaodi,

You make two drugs.

1. Deliberately making retroactive conformance outside of the file in which the type is declared illegal because of the problems it causes. See all the questions on Swift Users and watch people learning Swift get caught out.

2. Outside of the file in which the type is declared the static final extension is restricted to internal or fileprivate so that multiple modules can add static final extensions without clashes.

-- Howard.

On 11 Apr 2017, at 8:51 am, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Mon, Apr 10, 2017 at 5:35 PM, Howard Lovatt via swift-evolution <swift-evolution@swift.org> wrote:
In response to Jordan Rose's comment I suggest the following change:

Proposal: Split extension usage up into implementing methods and adding static functions

Currently extension methods are confusing because they have different dispatch rules for the same syntax. EG:

    protocol P {
        func m()
    }
    extension P {
        func m() { print("P.m") }
    }
    struct S: P {
        func m() { print("S.m") }
    }
    val p: P = S() // Note typed as P
    p.m() // Surprisingly prints P.m even though S implements its own m

This is incorrect. This prints "S.m", not "P.m".

    val s = S() // Note typed as S
    s.m() // Prints S.m as expected

This proposal cures the above problem by separating extension methods into two seperate use cases: implementations for methods and adding static functions.

First implementing methods.

If the extension is in the same file as the protocol/struct/class declaration then it implements the methods and is dispatched using a Vtable. EG:

File P.swift
    protocol/struct/class P {
        func m()
    }
    extension P {
        func m() { print("P.m") }
    }

Same or other file
    struct S: P {
        override func m() { print("S.m") } // Note override required because m already has an implementation from the extension

Requiring `override` breaks retroactive conformance of types to protocols. This idea has been brought up over half a dozen times. Each time it fails in not being able to accommodate retroactive conformance.

    }
    val p: P = S() // Note typed as P
    p.m() // Now prints S.m as expected

Extensions in the same file as the declaration can have any access, can be final, and can have where clauses and provide inheritable implementations.

The implementation needed to achieve this is that a value instance typed as a protocol is copied onto the heap, a pointer to its Vtable added, and it is passed as a pointer. IE it becomes a class instance. No change needed for a class instance typed as a protocol.

The second use case is adding static functions.

A new type of extension is proposed, a static final extension, which can be either in or outside the file in which the protocol/struct/class declaration is in. EG:

    static final extension P { // Note extension marked static final
        func m() { print("P.m") }
    }

Which is called as any other static function would be called:

    val s = S()
    P.m(s) // Prints P.m as expected

The new static final extension is shorthand, particularly in the case of multiple functions, for:

    extension P {
        static final func m(_ this: P) { print("P.m") }
    }

If the static final extension is outside of the file in which the protocol/struct/class declaration is in then the extension and the methods can only have fileprivate and internal access.

What is the use case for having this restriction? What is the problem you are trying to solve?

As at present protocol/struct/class can have both a static and instance method of the same name, m in the case of the example, because the usage syntax is distinct. As at present, static final extensions, both the extension and the individual functions, can have where clauses.

In summary.

The proposal formalises the split use of extensions into their two uses: implementing methods and adding static functions. Syntax is added that clarifies both for declarations and usage which type of extension is provided/in use.

Note the distinction between an extension in the same file and in a separate file is consistent with the proposed use of private in https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md.

Comments?

-- Howard.

On 7 Apr 2017, at 4:49 am, Jordan Rose <jordan_rose@apple.com> wrote:

[Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0164-remove-final-support-in-protocol-extensions.md]

On Apr 5, 2017, at 16:15, Howard Lovatt via swift-evolution <swift-evolution@swift.org> wrote:

The review of SE-0164 "Remove final support in protocol extensions"

What is your evaluation of the proposal?

The present situation isn't great. People get confused about which method will called with protocol extensions. Seems like every week there is a variation on this confusion on Swift Users mailing list. Therefore something needs to be done.

However I am not keen on this proposal since it makes behaviour inconsistent between methods in protocol extensions, classes, and structs.

I think a better solution would be one of the following alternatives:

  1. Must use final and final means it cannot be overridden; or
  2. If not final dispatches using a table like a class and if marked final cannot be overridden and if marked dynamic uses obj-c dispatching; or
  3. Must be marked dynamic and uses obj-c dispatching.

My preference would be option 2 but I think any of the three is superior to the present situation or the proposal.

People have suggested all of these before, but none of them are obviously correct. It's true that we have a difference between extension members that satisfy requirements and those that don't, and that that confuses people. However, an extension-only member of one protocol can be used to satisfy the requirements of another protocol today, which is a tool for code reuse.

(I think we managed to convince everyone that it's just a bug that a protocol extension method that satisfies a requirement cannot be overridden in a subclass, so at least that isn't an issue on top of the rest of this.)

Oh, and we can't retroactively add members of a protocol extension to existing adopters, which is why protocol extension members cannot be @objc. There are limited circumstances where that would be safe, but that would be a separate proposal.

Jordan

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


(Brent Royal-Gordon) #18

Howard, this would be very source-breaking and would fail to achieve fundamental goals of Swift's protocol design. Removing retroactive conformance is no more realistic than removing Objective-C bridging—another feature which introduces various ugly edge cases and tricky behaviors but is also non-negotiable.

···

On Apr 13, 2017, at 3:10 PM, Howard Lovatt via swift-evolution <swift-evolution@swift.org> wrote:

I don't see that retroactive conformance needs to be exportable. If it is exported then you cannot prevent clashes from two modules, this is a known problem in C#. Because of this and other problems with C# extensions, this style of extension were rejected by other language communities (notably Java and Scala).

A better alternative for export is a new class that encapsulates the standard type but with added methods for the protocol to be added. This way there is no clash between modules. EG:

    public protocol P {
        func m() -> String
    }
    public class PInt: P {
        var value = 0
        func m() -> String { return "PI.m" }
    }

--
Brent Royal-Gordon
Architechies


(Howard Lovatt) #19

@Brent,

I have updated the proposal to address your concerns, in particular I don't see that retrospectively adding methods and protocols has been removed it has just had its ugly corners rounded. See revised proposal below particularly the end of section "Retrospectively adding protocols and methods" and new section "Justification".

Hope this convinces you that the change is worthwhile.

-- Howard.

···

====================================

# Proposal: Split extension usage up into implementing methods and adding methods and protocols retrospectively

## Revision history

Version | Date | Comment |
---------|--------------|--------------|
Draft 1 | 11 April 2017 | Initial version |
Draft 2 | 13 April 2017 | Added support for post-hoc conformance to a protocol - replaced static final extensions with final extensions |
Draft 3 | 17 April 2017 | Added justification section |

## Introduction

Currently extension methods are confusing because they have different dispatch rules for the same calling syntax. EG:

    public protocol P {
        func mP() -> String
     }
    extension P {
        func mP() -> String { return "P.mP" }
        func mE() -> String { return "P.mE" }
    }
    struct S: P {
        func mP() -> String { return "S.mP" }
        func mE() -> String { return "S.mE" }
    }
    let s = S()
    s.mP() // S.mP as expected
    s.mE() // S.mE as expected
    let p: P = s // Note: s now typed as P
    p.mP() // S.mP as expected
    p.mE() // P.mE unexpected!

Extension methods can also cause compatibility problems between modules, consider:

In Module A
    extension Int: P {
        func m() -> String { print("A.m") }
    }

In Module B
    extension Int: P {
        func m() -> String { print("B.m") }
    }

In Module C
    import A
    import B // Should this be an error
    let i = 0
    i.m() // Should it return A.m or B.m?

This proposal cures the above two problems by separating extension methods into two seperate use cases: implementations for methods and adding methods and protocols retrospectively.

## Implementing methods

If the extension is in the same file as the protocol/struct/enum/class declaration then it implements the methods and is dispatched using a Vtable. EG:

File P.swift
    protocol/struct/enum/class P {
        // func m() not declared in type since it is added by the extension, under this proposal it is an error to include a declaration in a type *and* in an extension
    }
    extension P {
        func m() { print("P.m") } // m is added to the protocol/struct/enum/class declaration
    }

Same or other file
    struct S: P {
        override func m() { print("S.m") } // Note override required because m already has an implementation from the extension
    }
    let p: P = S() // Note typed as P
    p.m() // Now prints S.m as expected

Extensions in the same file as the declaration can have any access, can be final, and can have where clauses and provide inheritable implementations.

In a protocol at present there is a difference in behaviour between a protocol that declares a method that is then implemented in an extension and a protocol that just has the method implemented in an extension and no declaration. This situation only applies to protocols, for structs/enumerated/classes you cannot declare in type and implement in extensions. The proposal unifies the behaviour of protocol/struct/enum/class with extensions and prevents the error of a minor typo between the protocol and extension adding two methods instead of generating an error.

The implementation needed to achieve this proposal is that a value instance typed as a protocol is copied onto the heap, a pointer to its Vtable added, and it is passed as a pointer. IE it becomes a class instance. No change needed for a class instance typed as a protocol.

## Retrospectively adding protocols and methods

A new type of extension is proposed, a "final extension", which can be either in or outside the file in which the protocol/struct/enum/class declaration is in. EG:

    protocol P2 {
        func m2P()
    }
    final extension S: P2 { // Note extension marked final
        func m2P() { print("SP2.m2P") } // Implicitly final, completely implements P2
        func m2E() { print("SP2.m2E") } // Implicitly final, not an existing method
    }

Which are called as any other method would be called:

    let s = S()
    s.m2P() // Prints SP2.m2P
    s.m2E() // Prints SP2.m2E

A method added by a final extension is is implicitly final, as the name would suggest, and cannot be overridden.

Notes:

  1. If the final extension adds a method, e.g. m2E, that method cannot already exist. IE a final extension cannot override an existing method or implement a protocol declared method that lacks an implementation unless it also adds the protocol.

  2. If the final extension adds a protocol then it must implement all the methods in that protocol that are not currently implemented.

  3. If the final extension is outside of the file in which the protocol/struct/enum/class declaration is in then the extension and the methods can only have fileprivate or internal access. This prevents retrospective extensions from numerous modules clashing, since they are not exported outside of the module.

When a type is extended inside a module with a final extension the extension is not exported. For example:

    final extension Int: P2 {
        func m2P() { print("Int.m2P") }
    }

If an exported function uses Int, e.g.:

    public func f(_ x: Int) -> Int {
        x.m2P()
        return x
    }

Then when used in an external module both the input Int and the output Int are not extended with P2. However as the Int goes into f it gains P2 conformance and when it leaves it looses P2 conformance. Thus inside and outside the module the behaviour is easily understood and consistent and doesn't clash with other final extensions in other modules.

Taking the above example further an Int with P2 conformance is required by the user of a library; then it can simply and safely be provided, e.g.:

    public class P2Int: P2 {
        var value = 0
        func m2P() { print("Int.m2P") }
    }

This type, P2Int, is easy to write, one line longer than a final extension, and can easily be used as both a P2 and an Int and does not clash with another Int extension from another module.

## Justification

The aim of Swift is nothing more than dominating the world. Using the current, April 2017, https://www.tiobe.com/tiobe-index/ index of job adverts for programmers the languages that are in demand are: Java 15.568%, C 6.966%, C++ 4.554%, C# 3.579%, Python 3.457%, PHP 3.376%, Visual Basic .NET 3.251%, JavaScript 2.851%, Delphi/Object Pascal 2.816%, Perl 2.413%, Ruby 2.310%, and Swift 2.287%. So Swift at 12th is doing very well for a new language and is already above Objective-C at 14th. However there is obviously a long way to go and the purpose of this proposal is to help with this climb.

A characteristic of many of the languages above Swift in the Tiobe Index is that they have major third party libraries; for some languages they are almost defined by their third part libraries, e.g. Ruby for Rails. A major part of this proposal is to make extensions safe when using multiple libraries from different venders. In particular final extensions are not exported.

As part of Swift's goal of world domination is that it is meant to be easy to learn by a process of "successive disclosure". The current inconsistent behaviour of protocols and extensions hinders this process and is a common gotcha for newbies. This proposal eliminates that problem also.

Extensions are not new in languages, they are part of the .NET languages for example. Since .NET popularised extensions they have been discussed by other language communities, particularly Java and Scala, and in the academic community (normally termed the Expression Problem) however they have not proved popular because of the problems they cause. Nearly all languages have a strong bias towards keeping the language small and simple and trade of the advantages of a feature against the disadvantages and the feature only makes it into the language if it offers many advantages, has few disadvantages, and is not heavily overlapping with other features. This keeping it small and simple test is what extensions have failed in other languages.

Experience from .NET can however be used to improve extensions. There is some excellent advice https://blogs.msdn.microsoft.com/vbteam/2007/03/10/extension-methods-best-practices-extension-methods-part-6/ written by the VB .NET team when they added extensions to VB .NET. The best-practice advice can be summarised by the following quotes from the reference:

  0. "In most real world applications these suggestions [the rest of the suggestions] can (and quite frankly should!) be completely ignored." This is an important observations, in your own code that is not intended for reuse; go for it, use extensions. The proposal importantly still allows this style of programming and in fact improves it by adding consistent behaviour and syntax between protocols/structs/enumerated/classes.

1. "Read the .NET Framework Class Library Design Guidelines." The equivalent for Swift is lacking at this stage. Probably because third party libraries are rare.

  2. "Be wary of extension methods." This recommendation is formalised in the proposal by limiting final extensions to be fileprivate or internal.

  3. "Put extension methods into their own namespace." This recommendation is formalised in the proposal by limiting final extensions to be fileprivate or internal.

  4. "Think twice before extending types you don’t own."

  5. "Prefer interface extensions over class extensions." Translation to Swift terminology provide default implementations for protocol methods. The proposal encourages this by eliminating a major gotcha with the current implementation, namely the proposal always dispatches via a Vtable to give consistent behaviour.

  6. "Be as specific with the types you extend as possible." Translation to Swift terminology provide default implementations for protocol methods that extend other protocols if there is a more specific behaviour that is relevent. The proposal encourages this by eliminating a major gotcha with the current implementation, namely the proposal always dispatches via a Vtable to give consistent behaviour.

The proposal formalises these best practices from .NET whilst increasing consistence and without loosing the ability to use extensions heavily in your own one-off code to allow for rapid development. Most of the best practices are for better libraries, particularly third party, which is an important area for future Swift growth onto the server side. This proposal actively encourages this transition to large formal server side code without loosing the free wheeling nature of app code.

## Possible future work (not part of this proposal)

This proposal will naturally allow bodies to be added to protocols directly rather than via an extension, since under the proposal the extension adds the declaration to the type so it is a small step to allow the protocol methods to have an implementation.

In an opposite sense to the above adding bodies to protocols, extensions could be allowed to add method declarations without bodies to protocols.

The two above future work proposals, if both added, would add symmetry to where declarations and bodies may appear for protocols.

## In summary.

The proposal formalises the split use of extensions into their two uses: implementing methods and post-hoc adding protocols and methods. Syntax is added that clarifies the two use cases, the former are termed extensions and must be in the same file as the type is declared, and the latter are termed final extensions and can be in any file, however if they are not in the type's file the they can only have fileprivate or internal access.

Note the distinction between an extension in the same file and in a separate file is consistent with the philosophy that there is special status to the same file as proposed for private in https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md.

===================================================

#Proposal: Split extension usage up into implementing methods and adding methods and protocols post-hoc

Draft 2 (Added support for post-hoc conformance to a protocol - replaced static final extensions with final extensions)

## Introduction

Currently extension methods are confusing because they have different dispatch rules for the same calling syntax. EG:

    public protocol P {
        func mP() -> String
     }
    extension P {
        func mP() -> String { return "P.mP" }
        func mE() -> String { return "P.mE" }
    }
    struct S: P {
        func mP() -> String { return "S.mP" }
        func mE() -> String { return "S.mE" }
    }
    let s = S()
    s.mP() // S.mP as expected
    s.mE() // S.mE as expected
    let p: P = s // Note: s now typed as P
    p.mP() // S.mP as expected
    p.mE() // P.mE unexpected!

Extension methods can also cause compatibility problems between modules, consider:

In Module A
    extension Int: P {
        func m() -> String { print("A.m") }
    }

In Module B
    extension Int: P {
        func m() -> String { print("B.m") }
    }

In Module C
    import A
    import B // Should this be an error
    let i = 0
    i.m() // Should it return A.m or B.m?

This proposal cures the above two problems by separating extension methods into two seperate use cases: implementations for methods and adding methods and protocols post-hoc.

## Implementing methods

If the extension is in the same file as the protocol/struct/class declaration then it implements the methods and is dispatched using a Vtable. EG:

File P.swift
    protocol/struct/class P {
        // func m() not declared in type since it is added by the extension, under this proposal it is an error to include a declaration in a type *and* in an extension
    }
    extension P {
        func m() { print("P.m") } // m is added to the protocol/struct/class declaration
    }

Same or other file
    struct S: P {
        override func m() { print("S.m") } // Note override required because m already has an implementation from the extension
    }
    let p: P = S() // Note typed as P
    p.m() // Now prints S.m as expected

Extensions in the same file as the declaration can have any access, can be final, and can have where clauses and provide inheritable implementations.

In a protocol at present there is a difference in behaviour between a protocol that declares a method that is then implemented in an extension and a protocol that just has the method implemented in an extension and no declaration. This situation only applies to protocols, for structs and classes you cannot declare in type and implement in extensions. The proposal unifies the behaviour of protocol/struct/class with extensions and prevents the error of a minor typo between the protocol and extension adding two methods instead of generating an error.

The implementation needed to achieve this is that a value instance typed as a protocol is copied onto the heap, a pointer to its Vtable added, and it is passed as a pointer. IE it becomes a class instance. No change needed for a class instance typed as a protocol.

## Post-hoc adding protocols and methods

A new type of extension is proposed, a "final extension", which can be either in or outside the file in which the protocol/struct/class declaration is in. EG:

    protocol P2 {
        func m2P()
    }
    final extension S: P2 { // Note extension marked final
        func m2P() { print("SP2.m2P") } // Implicitly final, completely implements P2
        func m2E() { print("SP2.m2E") } // Implicitly final, not an existing method
    }

Which are called as any other method would be called:

    let s = S()
    s.m2P() // Prints SP2.m2P
    s.m2E() // Prints SP2.m2E

A method added by a final extension is is implicitly final, as the name would suggest, and cannot be overridden.

If the final extension:

  1. Adds a method, e.g. m2E, that method cannot already exist. IE a final extension cannot override an existing method or implement a protocol declared method that lacks an implementation unless it also post-hoc adds the protocol.

  2. Adds a protocol then it must implement all the methods in that protocol that are not currently implemented.

  3. Is outside of the file in which the protocol/struct/class declaration is in then the extension and the methods can only have fileprivate or internal access. This prevents post-hoc extensions from numerous modules clashing, since they are not exported outside of the module.

## Possible future work (not part of this proposal)

This proposal will naturally allow bodies to be added to protocols directly rather than via an extension, since under the proposal the extension adds the declaration to the type so it is a small step to allow the protocol methods to have an implementation.

In an opposite sense to the above adding bodies to protocols, extensions could be allowed to add method declarations without bodies to protocols.

The two above future work proposals, if both added, would add symmetry to where declarations and bodies may appear for protocols.

## In summary.

The proposal formalises the split use of extensions into their two uses: implementing methods and post-hoc adding protocols and methods. Syntax is added that clarifies the two use cases, the former are termed extensions and must be in the same file as the type is declared, and the latter are termed final extensions and can be in any file, however if they are not in the type's file the they can only have fileprivate or internal access.

Note the distinction between an extension in the same file and in a separate file is consistent with the philosophy that there is special status to the same file as proposed for private in https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md.

====================================

On 14 Apr 2017, at 8:17 am, Brent Royal-Gordon <brent@architechies.com> wrote:

On Apr 13, 2017, at 3:10 PM, Howard Lovatt via swift-evolution <swift-evolution@swift.org> wrote:

I don't see that retroactive conformance needs to be exportable. If it is exported then you cannot prevent clashes from two modules, this is a known problem in C#. Because of this and other problems with C# extensions, this style of extension were rejected by other language communities (notably Java and Scala).

A better alternative for export is a new class that encapsulates the standard type but with added methods for the protocol to be added. This way there is no clash between modules. EG:

   public protocol P {
       func m() -> String
   }
   public class PInt: P {
       var value = 0
       func m() -> String { return "PI.m" }
   }

Howard, this would be very source-breaking and would fail to achieve fundamental goals of Swift's protocol design. Removing retroactive conformance is no more realistic than removing Objective-C bridging—another feature which introduces various ugly edge cases and tricky behaviors but is also non-negotiable.

--
Brent Royal-Gordon
Architechies


(Xiaodi Wu) #20

This continues to forbid use cases that are critical.

For instance, I am writing a library that vends additional conformances for
Float and Double. Any numerics library would need to do the same.

Your design would eliminate all such libraries, which is a non-starter. I
am not sure what defects you are trying to solve with this proposal.

···

On Sun, Apr 16, 2017 at 17:51 Howard Lovatt <howard.lovatt@gmail.com> wrote:

@Brent,

I have updated the proposal to address your concerns, in particular I
don't see that retrospectively adding methods and protocols has been
removed it has just had its ugly corners rounded. See revised proposal
below particularly the end of section "Retrospectively adding protocols
and methods" and new section "Justification".

Hope this convinces you that the change is worthwhile.

-- Howard.

====================================

# Proposal: Split extension usage up into implementing methods and adding
methods and protocols retrospectively

## Revision history

> Version | Date | Comment |

>---------|--------------|--------------|

> Draft 1 | 11 April 2017 | Initial version |

> Draft 2 | 13 April 2017 | Added support for post-hoc conformance to a
protocol - replaced static final extensions with final extensions |

> Draft 3 | 17 April 2017 | Added justification section |

## Introduction

Currently extension methods are confusing because they have different
dispatch rules for the same calling syntax. EG:

    public protocol P {

        func mP() -> String

     }

    extension P {

        func mP() -> String { return "P.mP" }

        func mE() -> String { return "P.mE" }

    }

    struct S: P {

        func mP() -> String { return "S.mP" }

        func mE() -> String { return "S.mE" }

    }

    let s = S()

    s.mP() // S.mP as expected

    s.mE() // S.mE as expected

    let p: P = s // Note: s now typed as P

    p.mP() // S.mP as expected

    p.mE() // P.mE unexpected!

Extension methods can also cause compatibility problems between modules,
consider:

In Module A

    extension Int: P {

        func m() -> String { print("A.m") }

    }

In Module B

    extension Int: P {

        func m() -> String { print("B.m") }

    }

In Module C

    import A

    import B // Should this be an error

    let i = 0

    i.m() // Should it return A.m or B.m?

This proposal cures the above two problems by separating extension methods
into two seperate use cases: implementations for methods and adding methods
and protocols retrospectively.

## Implementing methods

If the extension is in the same file as the protocol/struct/enum/class
declaration then it implements the methods and is dispatched using a
Vtable. EG:

File P.swift

    protocol/struct/enum/class P {

        // func m() not declared in type since it is added by the
extension, under this proposal it is an error to include a declaration in a
type *and* in an extension

    }

    extension P {

        func m() { print("P.m") } // m is added to the
protocol/struct/enum/class declaration

    }

Same or other file

    struct S: P {

        override func m() { print("S.m") } // Note override required
because m already has an implementation from the extension

    }

    let p: P = S() // Note typed as P

    p.m() // Now prints S.m as expected

Extensions in the same file as the declaration can have any access, can be
final, and can have where clauses and provide inheritable implementations.

In a protocol at present there is a difference in behaviour between a
protocol that declares a method that is then implemented in an extension
and a protocol that just has the method implemented in an extension and no
declaration. This situation only applies to protocols, for
structs/enumerated/classes you cannot declare in type and implement in
extensions. The proposal unifies the behaviour of
protocol/struct/enum/class with extensions and prevents the error of a
minor typo between the protocol and extension adding two methods instead of
generating an error.

The implementation needed to achieve this proposal is that a value
instance typed as a protocol is copied onto the heap, a pointer to its
Vtable added, and it is passed as a pointer. IE it becomes a class
instance. No change needed for a class instance typed as a protocol.

## Retrospectively adding protocols and methods

A new type of extension is proposed, a "final extension", which can be
either in or outside the file in which the protocol/struct/enum/class
declaration is in. EG:

    protocol P2 {

        func m2P()

    }

    final extension S: P2 { // Note extension marked final

        func m2P() { print("SP2.m2P") } // Implicitly final, completely
implements P2

        func m2E() { print("SP2.m2E") } // Implicitly final, not an
existing method

    }

Which are called as any other method would be called:

    let s = S()

    s.m2P() // Prints SP2.m2P

    s.m2E() // Prints SP2.m2E

A method added by a final extension is is implicitly final, as the name
would suggest, and cannot be overridden.

Notes:

  1. If the final extension adds a method, e.g. m2E, that method cannot
already exist. IE a final extension cannot override an existing method or
implement a protocol declared method that lacks an implementation unless it
also adds the protocol.

  2. If the final extension adds a protocol then it must implement all the
methods in that protocol that are not currently implemented.

  3. If the final extension is outside of the file in which the
protocol/struct/enum/class declaration is in then the extension and the
methods can only have fileprivate or internal access. This prevents
retrospective extensions from numerous modules clashing, since they are not
exported outside of the module.

When a type is extended inside a module with a final extension the
extension is not exported. For example:

    final extension Int: P2 {

        func m2P() { print("Int.m2P") }

    }

If an exported function uses Int, e.g.:

    public func f(_ x: Int) -> Int {

        x.m2P()

        return x

    }

Then when used in an external module both the input Int and the output Int
are not extended with P2. However as the Int goes into f it gains P2
conformance and when it leaves it looses P2 conformance. Thus inside and
outside the module the behaviour is easily understood and consistent and
doesn't clash with other final extensions in other modules.

Taking the above example further an Int with P2 conformance is required by
the user of a library; then it can simply and safely be provided, e.g.:

    public class P2Int: P2 {

        var value = 0

        func m2P() { print("Int.m2P") }

    }

This type, P2Int, is easy to write, one line longer than a final
extension, and can easily be used as both a P2 and an Int and does not
clash with another Int extension from another module.

## Justification

The aim of Swift is nothing more than dominating the world. Using the
current, April 2017, https://www.tiobe.com/tiobe-index/ index of job
adverts for programmers the languages that are in demand are: Java 15.568%,
C 6.966%, C++ 4.554%, C# 3.579%, Python 3.457%, PHP 3.376%, Visual Basic
.NET 3.251%, JavaScript 2.851%, Delphi/Object Pascal 2.816%, Perl 2.413%,
Ruby 2.310%, and Swift 2.287%. So Swift at 12th is doing very well for a
new language and is already above Objective-C at 14th. However there is
obviously a long way to go and the purpose of this proposal is to help with
this climb.

A characteristic of many of the languages above Swift in the Tiobe Index
is that they have major third party libraries; for some languages they are
almost defined by their third part libraries, e.g. Ruby for Rails. A major
part of this proposal is to make extensions safe when using multiple
libraries from different venders. In particular final extensions are not
exported.

As part of Swift's goal of world domination is that it is meant to be easy
to learn by a process of "successive disclosure". The current inconsistent
behaviour of protocols and extensions hinders this process and is a common
gotcha for newbies. This proposal eliminates that problem also.

Extensions are not new in languages, they are part of the .NET languages
for example. Since .NET popularised extensions they have been discussed by
other language communities, particularly Java and Scala, and in the
academic community (normally termed the Expression Problem) however they
have not proved popular because of the problems they cause. Nearly all
languages have a strong bias towards keeping the language small and simple
and trade of the advantages of a feature against the disadvantages and the
feature only makes it into the language if it offers many advantages, has
few disadvantages, and is not heavily overlapping with other features. This
keeping it small and simple test is what extensions have failed in other
languages.

Experience from .NET can however be used to improve extensions. There is
some excellent advice
https://blogs.msdn.microsoft.com/vbteam/2007/03/10/extension-methods-best-practices-extension-methods-part-6/
written by the VB .NET team when they added extensions to VB .NET. The
best-practice advice can be summarised by the following quotes from the
reference:

  0. "In most real world applications these suggestions [the rest of the
suggestions] can (and quite frankly should!) be completely ignored." This
is an important observations, in your own code that is not intended for
reuse; go for it, use extensions. The proposal importantly still allows
this style of programming and in fact improves it by adding consistent
behaviour and syntax between protocols/structs/enumerated/classes.

1. "Read the .NET Framework Class Library Design Guidelines." The
equivalent for Swift is lacking at this stage. Probably because third party
libraries are rare.

  2. "Be wary of extension methods." This recommendation is formalised in
the proposal by limiting final extensions to be fileprivate or internal.

  3. "Put extension methods into their own namespace." This recommendation
is formalised in the proposal by limiting final extensions to be
fileprivate or internal.

  4. "Think twice before extending types you don’t own."

  5. "Prefer interface extensions over class extensions." Translation to
Swift terminology provide default implementations for protocol methods. The
proposal encourages this by eliminating a major gotcha with the current
implementation, namely the proposal always dispatches via a Vtable to give
consistent behaviour.

  6. "Be as specific with the types you extend as possible." Translation
to Swift terminology provide default implementations for protocol methods
that extend other protocols if there is a more specific behaviour that is
relevent. The proposal encourages this by eliminating a major gotcha with
the current implementation, namely the proposal always dispatches via a
Vtable to give consistent behaviour.

The proposal formalises these best practices from .NET whilst increasing
consistence and without loosing the ability to use extensions heavily in
your own one-off code to allow for rapid development. Most of the best
practices are for better libraries, particularly third party, which is an
important area for future Swift growth onto the server side. This proposal
actively encourages this transition to large formal server side code
without loosing the free wheeling nature of app code.

## Possible future work (not part of this proposal)

This proposal will naturally allow bodies to be added to protocols
directly rather than via an extension, since under the proposal the
extension adds the declaration to the type so it is a small step to allow
the protocol methods to have an implementation.

In an opposite sense to the above adding bodies to protocols, extensions
could be allowed to add method declarations without bodies to protocols.

The two above future work proposals, if both added, would add symmetry to
where declarations and bodies may appear for protocols.

## In summary.

The proposal formalises the split use of extensions into their two uses:
implementing methods and post-hoc adding protocols and methods. Syntax is
added that clarifies the two use cases, the former are termed extensions
and must be in the same file as the type is declared, and the latter are
termed final extensions and can be in any file, however if they are not in
the type's file the they can only have fileprivate or internal access.

Note the distinction between an extension in the same file and in a
separate file is consistent with the philosophy that there is special
status to the same file as proposed for private in
https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md
.

===================================================

#Proposal: Split extension usage up into implementing methods and adding
methods and protocols post-hoc

Draft 2 (Added support for post-hoc conformance to a protocol - replaced
static final extensions with final extensions)

## Introduction

Currently extension methods are confusing because they have different
dispatch rules for the same calling syntax. EG:

    public protocol P {

        func mP() -> String

     }

    extension P {

        func mP() -> String { return "P.mP" }

        func mE() -> String { return "P.mE" }

    }

    struct S: P {

        func mP() -> String { return "S.mP" }

        func mE() -> String { return "S.mE" }

    }

    let s = S()

    s.mP() // S.mP as expected

    s.mE() // S.mE as expected

    let p: P = s // Note: s now typed as P

    p.mP() // S.mP as expected

    p.mE() // P.mE unexpected!

Extension methods can also cause compatibility problems between modules,
consider:

In Module A

    extension Int: P {

        func m() -> String { print("A.m") }

    }

In Module B

    extension Int: P {

        func m() -> String { print("B.m") }

    }

In Module C

    import A

    import B // Should this be an error

    let i = 0

    i.m() // Should it return A.m or B.m?

This proposal cures the above two problems by separating extension methods
into two seperate use cases: implementations for methods and adding methods
and protocols post-hoc.

## Implementing methods

If the extension is in the same file as the protocol/struct/class
declaration then it implements the methods and is dispatched using a
Vtable. EG:

File P.swift

    protocol/struct/class P {

        // func m() not declared in type since it is added by the
extension, under this proposal it is an error to include a declaration in a
type *and* in an extension

    }

    extension P {

        func m() { print("P.m") } // m is added to the
protocol/struct/class declaration

    }

Same or other file

    struct S: P {

        override func m() { print("S.m") } // Note override required
because m already has an implementation from the extension

    }

    let p: P = S() // Note typed as P

    p.m() // Now prints S.m as expected

Extensions in the same file as the declaration can have any access, can be
final, and can have where clauses and provide inheritable implementations.

In a protocol at present there is a difference in behaviour between a
protocol that declares a method that is then implemented in an extension
and a protocol that just has the method implemented in an extension and no
declaration. This situation only applies to protocols, for structs and
classes you cannot declare in type and implement in extensions. The
proposal unifies the behaviour of protocol/struct/class with extensions and
prevents the error of a minor typo between the protocol and extension
adding two methods instead of generating an error.

The implementation needed to achieve this is that a value instance typed
as a protocol is copied onto the heap, a pointer to its Vtable added, and
it is passed as a pointer. IE it becomes a class instance. No change needed
for a class instance typed as a protocol.

## Post-hoc adding protocols and methods

A new type of extension is proposed, a "final extension", which can be
either in or outside the file in which the protocol/struct/class
declaration is in. EG:

    protocol P2 {

        func m2P()

    }

    final extension S: P2 { // Note extension marked final

        func m2P() { print("SP2.m2P") } // Implicitly final, completely
implements P2

        func m2E() { print("SP2.m2E") } // Implicitly final, not an
existing method

    }

Which are called as any other method would be called:

    let s = S()

    s.m2P() // Prints SP2.m2P

    s.m2E() // Prints SP2.m2E

A method added by a final extension is is implicitly final, as the name
would suggest, and cannot be overridden.

If the final extension:

  1. Adds a method, e.g. m2E, that method cannot already exist. IE a final
extension cannot override an existing method or implement a protocol
declared method that lacks an implementation unless it also post-hoc adds
the protocol.

  2. Adds a protocol then it must implement all the methods in that
protocol that are not currently implemented.

  3. Is outside of the file in which the protocol/struct/class declaration
is in then the extension and the methods can only have fileprivate or
internal access. This prevents post-hoc extensions from numerous modules
clashing, since they are not exported outside of the module.

## Possible future work (not part of this proposal)

This proposal will naturally allow bodies to be added to protocols
directly rather than via an extension, since under the proposal the
extension adds the declaration to the type so it is a small step to allow
the protocol methods to have an implementation.

In an opposite sense to the above adding bodies to protocols, extensions
could be allowed to add method declarations without bodies to protocols.

The two above future work proposals, if both added, would add symmetry to
where declarations and bodies may appear for protocols.

## In summary.

The proposal formalises the split use of extensions into their two uses:
implementing methods and post-hoc adding protocols and methods. Syntax is
added that clarifies the two use cases, the former are termed extensions
and must be in the same file as the type is declared, and the latter are
termed final extensions and can be in any file, however if they are not in
the type's file the they can only have fileprivate or internal access.

Note the distinction between an extension in the same file and in a
separate file is consistent with the philosophy that there is special
status to the same file as proposed for private in
https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md
.

====================================

On 14 Apr 2017, at 8:17 am, Brent Royal-Gordon <brent@architechies.com> > wrote:

On Apr 13, 2017, at 3:10 PM, Howard Lovatt via swift-evolution < > swift-evolution@swift.org> wrote:

I don't see that retroactive conformance needs to be exportable. If it is
exported then you cannot prevent clashes from two modules, this is a known
problem in C#. Because of this and other problems with C# extensions, this
style of extension were rejected by other language communities (notably
Java and Scala).

A better alternative for export is a new class that encapsulates the
standard type but with added methods for the protocol to be added. This way
there is no clash between modules. EG:

   public protocol P {

       func m() -> String

   }

   public class PInt: P {

       var value = 0

       func m() -> String { return "PI.m" }

   }

Howard, this would be very source-breaking and would fail to achieve
fundamental goals of Swift's protocol design. Removing retroactive
conformance is no more realistic than removing Objective-C bridging—another
feature which introduces various ugly edge cases and tricky behaviors but
is also non-negotiable.

--
Brent Royal-Gordon
Architechies