[swift evolution] [proposal] Proposal to add "implement" keyword to denote protocol method implementation


(Victor Gao) #1

Hello everybody. Please excuse this proposal’s poor formatting. I am very new to swift-evolution and don’t yet know how to do a formatted proposal. :slight_smile:

Proposal

I am proposing to add an implement keyword to go with protocol method implementations like the override keyword.

Motivation

When writing an implementation for a protocol method, there is no indication that the method is an implementation of a protocol method. Unless it is a well-known protocol like UITableViewDataSource, there is simply no way to know if the method is provided by a protocol or the enclosing class. Right now, the only way to guess if a method is protocol-provided is if it follows the pattern of someObjectOrView(_: requestSomething:) or someObjectOrView(_: somethingDidHappen:). But since non-Cocoa protocol methods may not follow that pattern, they will not be so easy to guess. Here’s an example illustrating the problem:

func recordInDatabase(database: TJDatabase, atIndexPath indexPath: NSIndexPath) -> TJRecord {
    //…
}

Is this a protocol method implementation, or simply a method declaration? Well, it looks like a protocol method implementation, but how can one be sure? There is no way to definitely know unless you are familiar with it or you found its declaration after searching in the whole project, worse in huge projects. The method above just seems too “ordinary” to be a protocol method implementation. Even worse, some developer might come around and even rename that method, and there would be an error, then he has to fish around for the protocol method he’s missing. Assuming that he finally found it (if the protocol is small enough), he might even implement it again with the same code as the renamed method. We can see the problem here.

Or, let’s think about this: how would it feel if there is no override keyword? How would one know if a method is an override or not? We can see how this relates to the confusion with protocol method implementations.

Proposed solution

The proposed solution is to add an implement keyword, which improves the code above to this:

implement func recordInDatabase(database: TJDatabase, atIndexPath indexPath: NSIndexPath) -> TJRecord {
    //…
}

Now it is very clear that we are implementing a protocol method rather than declaring a method. The code is much clearer, and it doesn’t hurt the readability either.

Detailed design

When overriding implemented protocol methods from a superclass, the override keyword is still used:

override func recordInDatabase(database: TJDatabase, atIndexPath indexPath: NSIndexPath) -> TJRecord {
    return super.recordInDatabase(database, atIndexPath: indexPath)
}

Impact on existing code

Existing code would be changed to include the implement keyword in appropriate places. This could be handled via the Swift latest syntax converter in Xcode.


(Jordan Rose) #2

I feel like this should be added to the "common proposal caveats", but please include a discussion of how this affects retroactive modeling (adding a protocol to a type you don't own that already has the appropriate members). Previous discussions about "implement" (usually "implements") usually fizzle out at this point.

(IIRC last time we got to "it's required if the member is in the same module, but allowed to be absent otherwise", which is fairly reasonable but probably still needs to be thought through.)

Jordan

···

On Mar 18, 2016, at 16:58 , Victor Gao via swift-evolution <swift-evolution@swift.org> wrote:

Hello everybody. Please excuse this proposal’s poor formatting. I am very new to swift-evolution and don’t yet know how to do a formatted proposal. :slight_smile:

Proposal

I am proposing to add an implement keyword to go with protocol method implementations like the override keyword.

Motivation

When writing an implementation for a protocol method, there is no indication that the method is an implementation of a protocol method. Unless it is a well-known protocol like UITableViewDataSource, there is simply no way to know if the method is provided by a protocol or the enclosing class. Right now, the only way to guess if a method is protocol-provided is if it follows the pattern of someObjectOrView(_: requestSomething:) or someObjectOrView(_: somethingDidHappen:). But since non-Cocoa protocol methods may not follow that pattern, they will not be so easy to guess. Here’s an example illustrating the problem:

func recordInDatabase(database: TJDatabase, atIndexPath indexPath: NSIndexPath) -> TJRecord {
    //…
}

Is this a protocol method implementation, or simply a method declaration? Well, it looks like a protocol method implementation, but how can one be sure? There is no way to definitely know unless you are familiar with it or you found its declaration after searching in the whole project, worse in huge projects. The method above just seems too “ordinary” to be a protocol method implementation. Even worse, some developer might come around and even rename that method, and there would be an error, then he has to fish around for the protocol method he’s missing. Assuming that he finally found it (if the protocol is small enough), he might even implement it again with the same code as the renamed method. We can see the problem here.

Or, let’s think about this: how would it feel if there is no override keyword? How would one know if a method is an override or not? We can see how this relates to the confusion with protocol method implementations.

Proposed solution

The proposed solution is to add an implement keyword, which improves the code above to this:

implement func recordInDatabase(database: TJDatabase, atIndexPath indexPath: NSIndexPath) -> TJRecord {
    //…
}

Now it is very clear that we are implementing a protocol method rather than declaring a method. The code is much clearer, and it doesn’t hurt the readability either.

Detailed design

When overriding implemented protocol methods from a superclass, the override keyword is still used:

override func recordInDatabase(database: TJDatabase, atIndexPath indexPath: NSIndexPath) -> TJRecord {
    return super.recordInDatabase(database, atIndexPath: indexPath)
}

Impact on existing code

Existing code would be changed to include the implement keyword in appropriate places. This could be handled via the Swift latest syntax converter in Xcode.

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


(Jordan Rose) #3

[+swift-evolution] Sure! It's just a fancy word for doing something like this:

protocol SelfAppendable {
  init()
  func +=(other: Self)
}

func *<Sequence: SelfAppendable >(sequence: Sequence, count: Int) -> Sequence {
  var result = Sequence()
  for _ in 0..<count {
    result += sequence
  }
  return result
}

extension Array: SelfAppendable {}
extension String: SelfAppendable {}

Both Array and String already have no-argument initializers and a += function, but they come from the standard library and don't (necessarily) satisfy any protocols, so they wouldn't be declared "implement". I can think of a few different ways to deal with this, but mostly I just want to make sure you (and everyone else) are considering this use case in your proposal. :slight_smile:

Best,
Jordan

···

On Mar 18, 2016, at 17:04 , Victor Gao <victorgao44@gmail.com> wrote:

Hi Jordan,

Could you explain more of what retroactive modeling is and how it affects the proposal? I am unfamiliar with retroactive modeling. Thanks!

Victor Gao.

On Mar 18, 2016, at 21:01, Jordan Rose <jordan_rose@apple.com <mailto:jordan_rose@apple.com>> wrote:

I feel like this should be added to the "common proposal caveats", but please include a discussion of how this affects retroactive modeling (adding a protocol to a type you don't own that already has the appropriate members). Previous discussions about "implement" (usually "implements") usually fizzle out at this point.

(IIRC last time we got to "it's required if the member is in the same module, but allowed to be absent otherwise", which is fairly reasonable but probably still needs to be thought through.)

Jordan

On Mar 18, 2016, at 16:58 , Victor Gao via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hello everybody. Please excuse this proposal’s poor formatting. I am very new to swift-evolution and don’t yet know how to do a formatted proposal. :slight_smile:

Proposal

I am proposing to add an implement keyword to go with protocol method implementations like the override keyword.

Motivation

When writing an implementation for a protocol method, there is no indication that the method is an implementation of a protocol method. Unless it is a well-known protocol like UITableViewDataSource, there is simply no way to know if the method is provided by a protocol or the enclosing class. Right now, the only way to guess if a method is protocol-provided is if it follows the pattern of someObjectOrView(_: requestSomething:) or someObjectOrView(_: somethingDidHappen:). But since non-Cocoa protocol methods may not follow that pattern, they will not be so easy to guess. Here’s an example illustrating the problem:

func recordInDatabase(database: TJDatabase, atIndexPath indexPath: NSIndexPath) -> TJRecord {
    //…
}

Is this a protocol method implementation, or simply a method declaration? Well, it looks like a protocol method implementation, but how can one be sure? There is no way to definitely know unless you are familiar with it or you found its declaration after searching in the whole project, worse in huge projects. The method above just seems too “ordinary” to be a protocol method implementation. Even worse, some developer might come around and even rename that method, and there would be an error, then he has to fish around for the protocol method he’s missing. Assuming that he finally found it (if the protocol is small enough), he might even implement it again with the same code as the renamed method. We can see the problem here.

Or, let’s think about this: how would it feel if there is no override keyword? How would one know if a method is an override or not? We can see how this relates to the confusion with protocol method implementations.

Proposed solution

The proposed solution is to add an implement keyword, which improves the code above to this:

implement func recordInDatabase(database: TJDatabase, atIndexPath indexPath: NSIndexPath) -> TJRecord {
    //…
}

Now it is very clear that we are implementing a protocol method rather than declaring a method. The code is much clearer, and it doesn’t hurt the readability either.

Detailed design

When overriding implemented protocol methods from a superclass, the override keyword is still used:

override func recordInDatabase(database: TJDatabase, atIndexPath indexPath: NSIndexPath) -> TJRecord {
    return super.recordInDatabase(database, atIndexPath: indexPath)
}

Impact on existing code

Existing code would be changed to include the implement keyword in appropriate places. This could be handled via the Swift latest syntax converter in Xcode.

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


(Ross O'Brien) #4

I recently had the misfortune of deleting what turned out to be a necessary
function, because it was an optional requirement of a protocol and wasn't
being called by any code in the project, so I would sympathise with this.

On the other hand I've also written protocols composed entirely of
functions that an established type already has, so I can retroactively have
the type conform. (For example, there's no 'ArithmeticOperation' protocol
which Int and Double both conform to, even though they both have a `+
(lhs:Self, rhs:Self) -> Self` operator, so you have to roll your own...
unless that should be a proposal itself.) It would be impossible to require
a function to use your 'implements' keyword to conform to a protocol in an
importing module.

···

On Fri, Mar 18, 2016 at 11:58 PM, Victor Gao via swift-evolution < swift-evolution@swift.org> wrote:

Hello everybody. Please excuse this proposal’s poor formatting. I am very
new to swift-evolution and don’t yet know how to do a formatted proposal. :slight_smile:

*Proposal*

I am proposing to add an implement keyword to go with protocol method
implementations like the override keyword.

*Motivation*

When writing an implementation for a protocol method, there is no
indication that the method is an implementation of a protocol method.
Unless it is a well-known protocol like UITableViewDataSource, there is
simply no way to know if the method is provided by a protocol or the
enclosing class. Right now, the only way to guess if a method is
protocol-provided is if it follows the pattern of someObjectOrView(_:
requestSomething:) or someObjectOrView(_: somethingDidHappen:). But since
non-Cocoa protocol methods may not follow that pattern, they will not be so
easy to guess. Here’s an example illustrating the problem:

func recordInDatabase(database: TJDatabase, atIndexPath indexPath:
NSIndexPath) -> TJRecord {
    //…
}

Is this a protocol method implementation, or simply a method declaration?
Well, it *looks* like a protocol method implementation, but how can one
be sure? There is no way to definitely know unless you are familiar with it
or you found its declaration after searching in the whole project, worse in
huge projects. The method above just seems too “ordinary” to be a protocol
method implementation. Even worse, some developer might come around and
even rename that method, and there would be an error, then he has to fish
around for the protocol method he’s missing. Assuming that he finally found
it (if the protocol is small enough), he might even implement it *again* with
the *same* code as the renamed method. We can see the problem here.

Or, let’s think about this: how would it feel if there is no override
keyword? How would one know if a method is an override or not? We can see
how this relates to the confusion with protocol method implementations.

*Proposed solution*

The proposed solution is to add an implement keyword, which improves the
code above to this:

implement func recordInDatabase(database: TJDatabase, atIndexPath
indexPath: NSIndexPath) -> TJRecord {
    //…
}

Now it is very clear that we are implementing a protocol method rather
than declaring a method. The code is much clearer, and it doesn’t hurt the
readability either.

*Detailed design*

When overriding implemented protocol methods from a superclass, the
override keyword is still used:

override func recordInDatabase(database: TJDatabase, atIndexPath
indexPath: NSIndexPath) -> TJRecord {
    return super.recordInDatabase(database, atIndexPath: indexPath)
}

*Impact on existing code*

Existing code would be changed to include the implement keyword in
appropriate places. This could be handled via the Swift latest syntax
converter in Xcode.

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


(Daniel Duan) #5

Victor Gao via swift-evolution <swift-evolution@...> writes:

Motivation

When writing an implementation for a protocol method, there is no indication
that the method is an implementation of a protocol method.

Yes there is. If a implementation of a protocol is incomplete, then the
compiler will complain. Also, knowing about a protocol before implementing it
seems like a good idea.

Also, I think that "looking up definitions is hard" is not a strong enough
reason to warrant this change.

A type can automatically fulfill requirements of some protocol right now. This
change would break that too.

So, -1.


(Victor Gao) #6

Do I need to create a md file in the swift evolution repository to post my proposal?

···

On Mar 19, 2016, at 00:12, Jordan Rose <jordan_rose@apple.com> wrote:

[+swift-evolution] Sure! It's just a fancy word for doing something like this:

protocol SelfAppendable {
  init()
  func +=(other: Self)
}

func *<Sequence: SelfAppendable >(sequence: Sequence, count: Int) -> Sequence {
  var result = Sequence()
  for _ in 0..<count {
    result += sequence
  }
  return result
}

extension Array: SelfAppendable {}
extension String: SelfAppendable {}

Both Array and String already have no-argument initializers and a += function, but they come from the standard library and don't (necessarily) satisfy any protocols, so they wouldn't be declared "implement". I can think of a few different ways to deal with this, but mostly I just want to make sure you (and everyone else) are considering this use case in your proposal. :slight_smile:

Best,
Jordan

On Mar 18, 2016, at 17:04 , Victor Gao <victorgao44@gmail.com <mailto:victorgao44@gmail.com>> wrote:

Hi Jordan,

Could you explain more of what retroactive modeling is and how it affects the proposal? I am unfamiliar with retroactive modeling. Thanks!

Victor Gao.


(Daniel Duan) #7

(cc swift-evolution)

The compiler can tell us when we have broken or not completed conformance, agreed.

If we can empower extensions a bit more, we could also fully group each protocol conformance by extension.

How would you “group” conformances for the following 3 protocols?

protocol A {
  func foo()
  func bar()
}
protocol B {
  func bar()
  func baz()
}
protocol C: Equatable {
  func bar()
}

···

On Mar 19, 2016, at 8:41 AM, Step C <schristopher@bignerdranch.com> wrote:


(Andrew Bennett) #8

In short yes, submit a PR, there's some documentation that deals with this:

How to propose a change
<https://github.com/apple/swift-evolution/blob/master/process.md#how-to-propose-a-change>

It may be worthwhile mentioning the PR number in this thread, and likewise
ensuring that your PR's description is a good brief summary linking back to
this thread.

···

On Sun, Mar 20, 2016 at 12:18 AM, Victor Gao via swift-evolution < swift-evolution@swift.org> wrote:

Do I need to create a md file in the swift evolution repository to post my
proposal?

On Mar 19, 2016, at 00:12, Jordan Rose <jordan_rose@apple.com> wrote:

[+swift-evolution] Sure! It's just a fancy word for doing something like
this:

protocol SelfAppendable {
  init()
  func +=(other: Self)
}

func *<Sequence: SelfAppendable >(sequence: Sequence, count: Int) ->
Sequence {
  var result = Sequence()
  for _ in 0..<count {
    result += sequence
  }
  return result
}

extension Array: SelfAppendable {}
extension String: SelfAppendable {}

Both Array and String already have no-argument initializers and a +=
function, but they come from the standard library and don't (necessarily)
satisfy any protocols, so they wouldn't be declared "implement". I can
think of a few different ways to deal with this, but mostly I just want to
make sure you (and everyone else) are considering this use case in your
proposal. :slight_smile:

Best,
Jordan

On Mar 18, 2016, at 17:04 , Victor Gao <victorgao44@gmail.com> wrote:

Hi Jordan,

Could you explain more of what retroactive modeling is and how it affects
the proposal? I am unfamiliar with retroactive modeling. Thanks!

Victor Gao.

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


(Step C) #9

It seems to me there are two options when you have overlapping protocol requirements:

1. Declare one extension that satisfies the group of overlapping protocols. In this case, perhaps A, B, and C.

2. Break up the extensions per protocol. In this case you have the problem that you have to pick which protocol an overlapping method implementation gets grouped with. Alternately, put those methods in a shared extension.

Either way this is far from a perfect solution. But I wonder if practically, it might cover most cases.

I am not sure there is a way to guarantee full coverage without annotating every protocol method. But given that we don't have optional requirements in protocols, The compiler will tell us if we remove a method we need. So we do not have a huge gap to cover, it seems to me.

···

On Mar 19, 2016, at 12:07 PM, Daniel Duan <daniel@duan.org> wrote:

(cc swift-evolution)

On Mar 19, 2016, at 8:41 AM, Step C <schristopher@bignerdranch.com> wrote:

The compiler can tell us when we have broken or not completed conformance, agreed.

If we can empower extensions a bit more, we could also fully group each protocol conformance by extension.

How would you “group” conformances for the following 3 protocols?

protocol A {
func foo()
func bar()
}
protocol B {
func bar()
func baz()
}
protocol C: Equatable {
func bar()
}