Modify optional method semantics for swift


(Carlos Rodriguez Dominguez) #1

Rationale:

As a everybody knows, “optional” keyword is present in swift solely to maintain compatibility with objective-c protocols. Optional methods in protocols are neither a swift capability, nor a wanted one. In fact, proposal [0070] intends to begin the transition to an optionals-free language. In fact, optionals, as presented by objective-c, are a manner to avoid interface segregation.

Nonetheless, in many occasions, optionals are used to model customized behavior vs default one. For instance, if you take a look at the documentation of UITableViewDataSource or delegate, you’ll see that optional methods are not required, since the framework provides a default behavior that can be customized by implementing the corresponding optional methods.

Consequently, is most cases, optional methods in objective-c are a means to replace the semantics of default protocol method implementations, as supported through extensions in swift.

Therefore, my proposal is to modify the semantics of “optional” keyword in swift to mean: “you must provide a default implementation of this method through an extension”. This is different from objective-c semantics, which mean “the implementation of this method may not be provided”.

Detailed design:

In this proposal, protocols could be defined like this:

protocol Datasource {
  associatedtype Element

  var count:Int {get}
  func elementAt(index:Int) -> Element
  optional func color(elementIndex:Int) -> UIColor
}

However, this definition enforces the developer to create an extension a provide a default implementation of the optional method:

extension Datasource {
  func color(elementIndex:Int) -> UIColor {
    return UIColor.blackColor()
  }
}

In this way, what we are achieving is that we are avoiding objective-c optional semantics (which is a way to avoid interface segregation), but we are making explicit that a method in a protocol requires a default implementation, thus not requiring the developer to re-implement the method in any entity adopting the protocol (as it currently happens when we provide a default method implementation). Moreover, we are making explicit that a certain protocol method has a default implementation, which can be confusing right now.

Note that in this proposal, the intention is to keep both "@objc optional” and simply “optional” keywords, to highlight both semantics. However, in order to avoid @objc optional semantics as much as possible (to be able to remove it in a future swift release), new annotations could be incorporated to optional methods in objective-c code, to specify the default returned value. For instance, the annotations could be like this:

@protocol Datasource
  -(NSInteger) numberOfElements;
  -(NSObject*) elementAtIndex:(NSInteger)index;

  @optional
  -(UIColor*) colorOfElementAtIndex:(NSInteger)index __attribute__((swift_default_value(“UIColor.blackColor()")));
@end

Note that this annotation also allows to better understand the default behavior in case the method is not implemented without reading the documentation. This annotation should produce a swift code similar to the above one.


(Vladimir) #2

IMO if a class conforms to some protocol, this *should* means that methods of the protocol is implemented exactly inside that class(or in superclasses). In your suggestion, *optional* methods will be implemented "somewhere" outside of conformed class. IMO protocol should not depend on any default implementation.

I have another suggestion: introduce 'optional' keyword to mark methods of protocol extension which(methods) were not declared in protocol. I.e. :

protocol A {
   func a()
   func b()
}

extension A {
   *optional* func c() {..} // explicitely tells us that this method is optional and was not introduces in the A protocol itself

   func a() {..} // default implementation for "existed" method in A protocol
}

···

On 09.05.2016 19:15, Carlos Rodríguez Domínguez via swift-evolution wrote:

Rationale:

As a everybody knows, “optional” keyword is present in swift solely to
maintain compatibility with objective-c protocols. Optional methods in
protocols are neither a swift capability, nor a wanted one. In fact,
proposal [0070] intends to begin the transition to an optionals-free
language. In fact, optionals, as presented by objective-c, are a manner to
avoid interface segregation.

Nonetheless, in many occasions, optionals are used to model customized
behavior vs default one. For instance, if you take a look at the
documentation of UITableViewDataSource or delegate, you’ll see that
optional methods are not required, since the framework provides a default
behavior that can be customized by implementing the corresponding optional
methods.

Consequently, is most cases, optional methods in objective-c are a means to
replace the semantics of default protocol method implementations, as
supported through extensions in swift.

Therefore, my proposal is to modify the semantics of “optional” keyword in
swift to mean: “you must provide a default implementation of this method
through an extension”. This is different from objective-c semantics, which
mean “the implementation of this method may not be provided”.

Detailed design:

In this proposal, protocols could be defined like this:

protocol Datasource {
associatedtype Element

var count:Int {get}
func elementAt(index:Int) -> Element
optional func color(elementIndex:Int) -> UIColor
}

However, this definition enforces the developer to create an extension a
provide a default implementation of the optional method:

extension Datasource {
func color(elementIndex:Int) -> UIColor {
return UIColor.blackColor()
}

In this way, what we are achieving is that we are avoiding objective-c
optional semantics (which is a way to avoid interface segregation), but we
are making explicit that a method in a protocol requires a default
implementation, thus not requiring the developer to re-implement the method
in any entity adopting the protocol (as it currently happens when we
provide a default method implementation). Moreover, we are making explicit
that a certain protocol method has a default implementation, which can be
confusing right now.

Note that in this proposal, the intention is to keep both "@objc optional”
and simply “optional” keywords, to highlight both semantics. However, in
order to avoid @objc optional semantics as much as possible (to be able to
remove it in a future swift release), new annotations could be incorporated
to optional methods in objective-c code, to specify the default returned
value. For instance, the annotations could be like this:

@protocol Datasource
-(NSInteger) numberOfElements;
-(NSObject*) elementAtIndex:(NSInteger)index;

@optional
-(UIColor*) colorOfElementAtIndex:(NSInteger)index
__attribute__((swift_default_value(“UIColor.blackColor()")));
@end

Note that this annotation also allows to better understand the default
behavior in case the method is not implemented without reading the
documentation. This annotation should produce a swift code similar to the
above one.

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


(Rod Brown) #3

This issue I see here, and one that people seem to often forget in discussions on Swift a Evolution, is that optional methods aren't just used to change a default value. Their existence often actually changes behavior completely.

Take for example UITableViewDelegate and its method "tableView:heightForRowAtIndexPath:".

If you implement this method, every cell can have a variable height, and the table cannot optimize for it, so the math, storage and management of the data is specific to every row. This method will be called for every cell in the table view at load, because the table must calculate that size to compute the content size.

If you do not implement this method, the cell sizing is simple. Table view doesn't have to call this method again and again, but not only that: it can actually optimise the layout with the knowledge that there is no variable sizing. That makes the mathematics of content size so much easier to compute, and results in a far better performing table view, down to even working at 60fps on an original iPhone.

UITableView has optimized the variable sizing problems recently with estimated values that allow the table to "estimate" the sizing, and then adjust appropriately its details as the cells load. This also how self-sizing cells work. That said, it's an avoidance to deal with the limitations that the method creates: if it exists on the delegate, you're opting into a much slower and less efficient sizing path.

The problem then becomes how do we model such optimizations in Swift. Default methods, while useful for plenty of cases, don't work with this system as there is no runtime check to see if the object actually implements that method. The optimizations don't just check the existence for a default value - they actually lead to different code paths.

Can this be done? Absolutely. Either by segregating the protocols as you mentioned, or by adding some other way to cue the optimized sizing path. That said, it's certainly not as clean as the Objective-C design.

In general, I think default implementations fix a lot of the simpler cases surrounding optional methods and actually simplify other implementations (no runtime checks!). But they don't solve the real value proposition that the fully dynamic Objective-C design provided in this feature.

Do I support default implementations on protocols as a concept? Definitely. But I don't think we should kid ourselves that this solves anything apart from the simpler cases.

- Rod

···

On 10 May 2016, at 2:15 AM, Carlos Rodríguez Domínguez via swift-evolution <swift-evolution@swift.org> wrote:

Rationale:

As a everybody knows, “optional” keyword is present in swift solely to maintain compatibility with objective-c protocols. Optional methods in protocols are neither a swift capability, nor a wanted one. In fact, proposal [0070] intends to begin the transition to an optionals-free language. In fact, optionals, as presented by objective-c, are a manner to avoid interface segregation.

Nonetheless, in many occasions, optionals are used to model customized behavior vs default one. For instance, if you take a look at the documentation of UITableViewDataSource or delegate, you’ll see that optional methods are not required, since the framework provides a default behavior that can be customized by implementing the corresponding optional methods.

Consequently, is most cases, optional methods in objective-c are a means to replace the semantics of default protocol method implementations, as supported through extensions in swift.

Therefore, my proposal is to modify the semantics of “optional” keyword in swift to mean: “you must provide a default implementation of this method through an extension”. This is different from objective-c semantics, which mean “the implementation of this method may not be provided”.

Detailed design:

In this proposal, protocols could be defined like this:

protocol Datasource {
  associatedtype Element

  var count:Int {get}
  func elementAt(index:Int) -> Element
  optional func color(elementIndex:Int) -> UIColor
}

However, this definition enforces the developer to create an extension a provide a default implementation of the optional method:

extension Datasource {
  func color(elementIndex:Int) -> UIColor {
    return UIColor.blackColor()
  }
}

In this way, what we are achieving is that we are avoiding objective-c optional semantics (which is a way to avoid interface segregation), but we are making explicit that a method in a protocol requires a default implementation, thus not requiring the developer to re-implement the method in any entity adopting the protocol (as it currently happens when we provide a default method implementation). Moreover, we are making explicit that a certain protocol method has a default implementation, which can be confusing right now.

Note that in this proposal, the intention is to keep both "@objc optional” and simply “optional” keywords, to highlight both semantics. However, in order to avoid @objc optional semantics as much as possible (to be able to remove it in a future swift release), new annotations could be incorporated to optional methods in objective-c code, to specify the default returned value. For instance, the annotations could be like this:

@protocol Datasource
  -(NSInteger) numberOfElements;
  -(NSObject*) elementAtIndex:(NSInteger)index;

  @optional
  -(UIColor*) colorOfElementAtIndex:(NSInteger)index __attribute__((swift_default_value(“UIColor.blackColor()")));
@end

Note that this annotation also allows to better understand the default behavior in case the method is not implemented without reading the documentation. This annotation should produce a swift code similar to the above one.

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


(Matthew Johnson) #4

This issue I see here, and one that people seem to often forget in discussions on Swift a Evolution, is that optional methods aren't just used to change a default value. Their existence often actually changes behavior completely.

Take for example UITableViewDelegate and its method "tableView:heightForRowAtIndexPath:".

If you implement this method, every cell can have a variable height, and the table cannot optimize for it, so the math, storage and management of the data is specific to every row. This method will be called for every cell in the table view at load, because the table must calculate that size to compute the content size.

If you do not implement this method, the cell sizing is simple. Table view doesn't have to call this method again and again, but not only that: it can actually optimise the layout with the knowledge that there is no variable sizing. That makes the mathematics of content size so much easier to compute, and results in a far better performing table view, down to even working at 60fps on an original iPhone.

UITableView has optimized the variable sizing problems recently with estimated values that allow the table to "estimate" the sizing, and then adjust appropriately its details as the cells load. This also how self-sizing cells work. That said, it's an avoidance to deal with the limitations that the method creates: if it exists on the delegate, you're opting into a much slower and less efficient sizing path.

The problem then becomes how do we model such optimizations in Swift. Default methods, while useful for plenty of cases, don't work with this system as there is no runtime check to see if the object actually implements that method. The optimizations don't just check the existence for a default value - they actually lead to different code paths.

Can this be done? Absolutely. Either by segregating the protocols as you mentioned, or by adding some other way to cue the optimized sizing path. That said, it's certainly not as clean as the Objective-C design.

Clean is in the eye of the beholder. I definitely don’t think the Objective-C design feels right in Swift.

There have been times where I implemented delegates where the row height strategy was decided at initialization time. In Objective-C I was able to do this by implementing respondsToSelector. That feels a bit dirty, but it works. In pure Swift there is no way to do that at all.

Swift allows a delegate protocol design to take a position on whether the initialization-time decision is supported or is a code smell that should be disallowed. The approach of segregated protocols disallows it. Alternative mechanisms such as a boolean getter allow for it.

In general, I think default implementations fix a lot of the simpler cases surrounding optional methods and actually simplify other implementations (no runtime checks!). But they don't solve the real value proposition that the fully dynamic Objective-C design provided in this feature.

Do I support default implementations on protocols as a concept? Definitely. But I don't think we should kid ourselves that this solves anything apart from the simpler cases.

I agree with this. I think we should study the more complex cases and establish guidelines around idiomatic patterns for them in Swift. This may even result in proposals for language enhancements to support the idioms we want to see in Swift. But I’m glad to see “optional requirements” going away.

-Matthew

···

On May 9, 2016, at 5:13 PM, Rod Brown via swift-evolution <swift-evolution@swift.org> wrote:

- Rod

On 10 May 2016, at 2:15 AM, Carlos Rodríguez Domínguez via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Rationale:

As a everybody knows, “optional” keyword is present in swift solely to maintain compatibility with objective-c protocols. Optional methods in protocols are neither a swift capability, nor a wanted one. In fact, proposal [0070] intends to begin the transition to an optionals-free language. In fact, optionals, as presented by objective-c, are a manner to avoid interface segregation.

Nonetheless, in many occasions, optionals are used to model customized behavior vs default one. For instance, if you take a look at the documentation of UITableViewDataSource or delegate, you’ll see that optional methods are not required, since the framework provides a default behavior that can be customized by implementing the corresponding optional methods.

Consequently, is most cases, optional methods in objective-c are a means to replace the semantics of default protocol method implementations, as supported through extensions in swift.

Therefore, my proposal is to modify the semantics of “optional” keyword in swift to mean: “you must provide a default implementation of this method through an extension”. This is different from objective-c semantics, which mean “the implementation of this method may not be provided”.

Detailed design:

In this proposal, protocols could be defined like this:

protocol Datasource {
  associatedtype Element

  var count:Int {get}
  func elementAt(index:Int) -> Element
  optional func color(elementIndex:Int) -> UIColor
}

However, this definition enforces the developer to create an extension a provide a default implementation of the optional method:

extension Datasource {
  func color(elementIndex:Int) -> UIColor {
    return UIColor.blackColor()
  }
}

In this way, what we are achieving is that we are avoiding objective-c optional semantics (which is a way to avoid interface segregation), but we are making explicit that a method in a protocol requires a default implementation, thus not requiring the developer to re-implement the method in any entity adopting the protocol (as it currently happens when we provide a default method implementation). Moreover, we are making explicit that a certain protocol method has a default implementation, which can be confusing right now.

Note that in this proposal, the intention is to keep both "@objc optional” and simply “optional” keywords, to highlight both semantics. However, in order to avoid @objc optional semantics as much as possible (to be able to remove it in a future swift release), new annotations could be incorporated to optional methods in objective-c code, to specify the default returned value. For instance, the annotations could be like this:

@protocol Datasource
  -(NSInteger) numberOfElements;
  -(NSObject*) elementAtIndex:(NSInteger)index;

  @optional
  -(UIColor*) colorOfElementAtIndex:(NSInteger)index __attribute__((swift_default_value(“UIColor.blackColor()")));
@end

Note that this annotation also allows to better understand the default behavior in case the method is not implemented without reading the documentation. This annotation should produce a swift code similar to the above one.

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

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


(Carlos Rodriguez Dominguez) #5

That’s a very interesting approach. My approach was intended to facilitate the transition from @objc optionals. However, your proposal is good for me too.

···

El 9 may 2016, a las 18:54, Vladimir.S <svabox@gmail.com> escribió:

IMO if a class conforms to some protocol, this *should* means that methods of the protocol is implemented exactly inside that class(or in superclasses). In your suggestion, *optional* methods will be implemented "somewhere" outside of conformed class. IMO protocol should not depend on any default implementation.

I have another suggestion: introduce 'optional' keyword to mark methods of protocol extension which(methods) were not declared in protocol. I.e. :

protocol A {
func a()
func b()
}

extension A {
*optional* func c() {..} // explicitely tells us that this method is optional and was not introduces in the A protocol itself

func a() {..} // default implementation for "existed" method in A protocol
}

On 09.05.2016 19:15, Carlos Rodríguez Domínguez via swift-evolution wrote:

Rationale:

As a everybody knows, “optional” keyword is present in swift solely to
maintain compatibility with objective-c protocols. Optional methods in
protocols are neither a swift capability, nor a wanted one. In fact,
proposal [0070] intends to begin the transition to an optionals-free
language. In fact, optionals, as presented by objective-c, are a manner to
avoid interface segregation.

Nonetheless, in many occasions, optionals are used to model customized
behavior vs default one. For instance, if you take a look at the
documentation of UITableViewDataSource or delegate, you’ll see that
optional methods are not required, since the framework provides a default
behavior that can be customized by implementing the corresponding optional
methods.

Consequently, is most cases, optional methods in objective-c are a means to
replace the semantics of default protocol method implementations, as
supported through extensions in swift.

Therefore, my proposal is to modify the semantics of “optional” keyword in
swift to mean: “you must provide a default implementation of this method
through an extension”. This is different from objective-c semantics, which
mean “the implementation of this method may not be provided”.

Detailed design:

In this proposal, protocols could be defined like this:

protocol Datasource {
associatedtype Element

var count:Int {get}
func elementAt(index:Int) -> Element
optional func color(elementIndex:Int) -> UIColor
}

However, this definition enforces the developer to create an extension a
provide a default implementation of the optional method:

extension Datasource {
func color(elementIndex:Int) -> UIColor {
return UIColor.blackColor()
}
}

In this way, what we are achieving is that we are avoiding objective-c
optional semantics (which is a way to avoid interface segregation), but we
are making explicit that a method in a protocol requires a default
implementation, thus not requiring the developer to re-implement the method
in any entity adopting the protocol (as it currently happens when we
provide a default method implementation). Moreover, we are making explicit
that a certain protocol method has a default implementation, which can be
confusing right now.

Note that in this proposal, the intention is to keep both "@objc optional”
and simply “optional” keywords, to highlight both semantics. However, in
order to avoid @objc optional semantics as much as possible (to be able to
remove it in a future swift release), new annotations could be incorporated
to optional methods in objective-c code, to specify the default returned
value. For instance, the annotations could be like this:

@protocol Datasource
-(NSInteger) numberOfElements;
-(NSObject*) elementAtIndex:(NSInteger)index;

@optional
-(UIColor*) colorOfElementAtIndex:(NSInteger)index
__attribute__((swift_default_value(“UIColor.blackColor()")));
@end

Note that this annotation also allows to better understand the default
behavior in case the method is not implemented without reading the
documentation. This annotation should produce a swift code similar to the
above one.

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


(Rod Brown) #6

Response inline

The problem then becomes how do we model such optimizations in Swift. Default methods, while useful for plenty of cases, don't work with this system as there is no runtime check to see if the object actually implements that method. The optimizations don't just check the existence for a default value - they actually lead to different code paths.

Can this be done? Absolutely. Either by segregating the protocols as you mentioned, or by adding some other way to cue the optimized sizing path. That said, it's certainly not as clean as the Objective-C design.

Clean is in the eye of the beholder. I definitely don’t think the Objective-C design feels right in Swift.

There have been times where I implemented delegates where the row height strategy was decided at initialization time. In Objective-C I was able to do this by implementing respondsToSelector. That feels a bit dirty, but it works. In pure Swift there is no way to do that at all.

Swift allows a delegate protocol design to take a position on whether the initialization-time decision is supported or is a code smell that should be disallowed. The approach of segregated protocols disallows it. Alternative mechanisms such as a boolean getter allow for it.

I agree with this completely. By clean I was referring to the fact it's more concise, not that it's a good fit for the language. I don't think we should adopt this feature in Swift without seriously considering the implications because the nature of Swift's protocols is very different to that of Obj-C. I suspect there are better ways to model this behavior without breaking the philosophical underpinnings of Swift Protocols.

In general, I think default implementations fix a lot of the simpler cases surrounding optional methods and actually simplify other implementations (no runtime checks!). But they don't solve the real value proposition that the fully dynamic Objective-C design provided in this feature.

Do I support default implementations on protocols as a concept? Definitely. But I don't think we should kid ourselves that this solves anything apart from the simpler cases.

I agree with this. I think we should study the more complex cases and establish guidelines around idiomatic patterns for them in Swift. This may even result in proposals for language enhancements to support the idioms we want to see in Swift. But I’m glad to see “optional requirements” going away.

-Matthew

I don't think they're going away at this time, so much as we are clarifying what is already the case: they are an Obj-C only feature, and aren't supported in Swift standalone. They make sense and will continue to make sense in an Obj-C world, but they're not a foundational element of Swift.

···

On 10 May 2016, at 8:28 AM, Matthew Johnson <matthew@anandabits.com> wrote:

On May 9, 2016, at 5:13 PM, Rod Brown via swift-evolution <swift-evolution@swift.org> wrote:


(Matthew Johnson) #7

Response inline

The problem then becomes how do we model such optimizations in Swift. Default methods, while useful for plenty of cases, don't work with this system as there is no runtime check to see if the object actually implements that method. The optimizations don't just check the existence for a default value - they actually lead to different code paths.

Can this be done? Absolutely. Either by segregating the protocols as you mentioned, or by adding some other way to cue the optimized sizing path. That said, it's certainly not as clean as the Objective-C design.

Clean is in the eye of the beholder. I definitely don’t think the Objective-C design feels right in Swift.

There have been times where I implemented delegates where the row height strategy was decided at initialization time. In Objective-C I was able to do this by implementing respondsToSelector. That feels a bit dirty, but it works. In pure Swift there is no way to do that at all.

Swift allows a delegate protocol design to take a position on whether the initialization-time decision is supported or is a code smell that should be disallowed. The approach of segregated protocols disallows it. Alternative mechanisms such as a boolean getter allow for it.

I agree with this completely. By clean I was referring to the fact it's more concise, not that it's a good fit for the language. I don't think we should adopt this feature in Swift without seriously considering the implications because the nature of Swift's protocols is very different to that of Obj-C. I suspect there are better ways to model this behavior without breaking the philosophical underpinnings of Swift Protocols.

In general, I think default implementations fix a lot of the simpler cases surrounding optional methods and actually simplify other implementations (no runtime checks!). But they don't solve the real value proposition that the fully dynamic Objective-C design provided in this feature.

Do I support default implementations on protocols as a concept? Definitely. But I don't think we should kid ourselves that this solves anything apart from the simpler cases.

I agree with this. I think we should study the more complex cases and establish guidelines around idiomatic patterns for them in Swift. This may even result in proposals for language enhancements to support the idioms we want to see in Swift. But I’m glad to see “optional requirements” going away.

-Matthew

I don't think they're going away at this time, so much as we are clarifying what is already the case: they are an Obj-C only feature, and aren't supported in Swift standalone. They make sense and will continue to make sense in an Obj-C world, but they're not a foundational element of Swift.

Agree. I said going away with an indefinite, likely long time frame in mind. :slight_smile:

···

Sent from my iPad

On May 9, 2016, at 5:53 PM, Rod Brown <rodney.brown6@icloud.com> wrote:

On 10 May 2016, at 8:28 AM, Matthew Johnson <matthew@anandabits.com> wrote:

On May 9, 2016, at 5:13 PM, Rod Brown via swift-evolution <swift-evolution@swift.org> wrote: