[Proposal] Make optional protocol methods first class citizens


(Thorsten Seitz) #1

Just a thought: optional methods could be modeled by methods in a protocol that return optional closures.

-Thorsten

···

Am 31. März 2016 um 04:42 schrieb Andrey Tarantsov via swift-evolution swift-evolution@swift.org:

On Mar 30, 2016, at 8:08 PM, Yuval Tal via swift-evolution swift-evolution@swift.org wrote:

Hi,

I find that optional protocol methods to be very useful. However, there is a caveat – it needs to be mapped to @objc. This puts a set of limitations, such as: structures cannot be used as parameters as it does not map to objective-c. What do you think about removing the requirement of using @objc and allow to create optional methods without these limitations?

Thank you,

-Yuval


swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution
I’m missing those optional methods too, but protocol extensions sound like a better solution for this.

(For those suggesting a separate protocol, consider UITableView. How many protocols would it take to model all the optional delegate methods as separate protocols? Certainly more than 10, perhaps a few dozen.)

I would welcome a standardized way to document the methods as optional-to-implement, though, beyond just requiring a protocol extension. My ideal option would be to allow the optional keyword and change it to simply require a default implementation in a protocol extension.

If we don’t want a language change, then perhaps a conventional doc tag?

A.


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


(Sean Heber) #2

Some ideas I was thinking about for optional protocol functions was you’d declare it like this:

protocol UIGestureRecognizerDelegate {
  optional func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool = true
}

If you put “optional” there, the compiler would auto-generate an empty function in a protocol extension in your module as if you had specified one yourself. If the function has a return value, you would be required to specify the default return value in the protocol which provides automatic documentation of the default, too. (It would be an error to have an optional function with a return value that doesn’t have a default value specified in the protocol.) (I am not currently in favor of supplying a default implementation body inside of a protocol as has been discussed some previously.)

If you provide a default implementation of an optional protocol function in a protocol extension in the same module as the protocol itself is declared in, it would be an error and you’d have to make the function non-optional or get rid of your conflicting protocol extension implementation. If the protocol was declared in a different module, then that restriction would be lifted since presumably you’re providing a default implementation via protocol extension on purpose.

l8r
Sean

···

On Mar 31, 2016, at 1:34 AM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org> wrote:

Just a thought: optional methods could be modeled by methods in a protocol that return optional closures.

-Thorsten

Am 31. März 2016 um 04:42 schrieb Andrey Tarantsov via swift-evolution <swift-evolution@swift.org>:

I'm missing those optional methods too, but protocol extensions sound like a better solution for this.

(For those suggesting a separate protocol, consider UITableView. How many protocols would it take to model all the optional delegate methods as separate protocols? Certainly more than 10, perhaps a few dozen.)

I would welcome a standardized way to document the methods as optional-to-implement, though, beyond just requiring a protocol extension. My ideal option would be to allow the optional keyword and change it to simply require a default implementation in a protocol extension.

If we don't want a language change, then perhaps a conventional doc tag?

A.

On Mar 30, 2016, at 8:08 PM, Yuval Tal via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

I find that optional protocol methods to be very useful. However, there is a caveat -- it needs to be mapped to @objc. This puts a set of limitations, such as: structures cannot be used as parameters as it does not map to objective-c. What do you think about removing the requirement of using @objc and allow to create optional methods without these limitations?

Thank you,

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

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

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


(Thorsten Seitz) #3

Some ideas I was thinking about for optional protocol functions was you’d declare it like this:

protocol UIGestureRecognizerDelegate {
optional func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool = true
}

Why not just the following

import Foundation

// optional delegate method

protocol UIGestureRecognizerDelegate {
    var gestureRecognizerShouldBegin: ((gestureRecognizer: UIGestureRecognizer) -> Bool)? { get }
}

extension UIGestureRecognizerDelegate {
    var gestureRecognizerShouldBegin: ((gestureRecognizer: UIGestureRecognizer) -> Bool)? {
        return nil
    }
}

class UIGestureRecognizer {
    
    var delegate: UIGestureRecognizerDelegate?
    
    func callDelegate() {
        if let gestureRecognizerShouldBegin = delegate?.gestureRecognizerShouldBegin {
            gestureRecognizerShouldBegin(gestureRecognizer: self)
        }
    }
}

// implementing a delegate:

struct MyDelegate : UIGestureRecognizerDelegate {
    var gestureRecognizerShouldBegin: ((gestureRecognizer: UIGestureRecognizer) -> Bool)? {
        return { (gestureRecognizer: UIGestureRecognizer) -> Bool in
            return true
        }
    }
}

struct MyOtherDelegate : UIGestureRecognizerDelegate { }

This captures the idea of optional methods as used in Objective-C quite nicely IMO. And it already works without needing a new language feature.
If/when Swift gains the ability to declare default implementations within a protocol instead of needing an extension the boilerplate of having to repeat everything in the extension would drop away.

If you put “optional” there, the compiler would auto-generate an empty function in a protocol extension in your module as if you had specified one yourself. If the function has a return value, you would be required to specify the default return value in the protocol which provides automatic documentation of the default, too. (It would be an error to have an optional function with a return value that doesn’t have a default value specified in the protocol.) (I am not currently in favor of supplying a default implementation body inside of a protocol as has been discussed some previously.)

I do not understand why an optional method should require a default value. That’s not how optional methods work in Objective-C where the caller will ask whether the method is implemented and calls it or does something else. With a default value it would be much more difficult to do something different if the method is not implemented.
Actually with a default value the method would just be a normal method which has not been overridden, there would be nothing optional about it anymore.

-Thorsten

···

Am 31.03.2016 um 17:18 schrieb Sean Heber <sean@fifthace.com>:

If you provide a default implementation of an optional protocol function in a protocol extension in the same module as the protocol itself is declared in, it would be an error and you’d have to make the function non-optional or get rid of your conflicting protocol extension implementation. If the protocol was declared in a different module, then that restriction would be lifted since presumably you’re providing a default implementation via protocol extension on purpose.

l8r
Sean

On Mar 31, 2016, at 1:34 AM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org> wrote:

Just a thought: optional methods could be modeled by methods in a protocol that return optional closures.

-Thorsten

Am 31. März 2016 um 04:42 schrieb Andrey Tarantsov via swift-evolution <swift-evolution@swift.org>:

I'm missing those optional methods too, but protocol extensions sound like a better solution for this.

(For those suggesting a separate protocol, consider UITableView. How many protocols would it take to model all the optional delegate methods as separate protocols? Certainly more than 10, perhaps a few dozen.)

I would welcome a standardized way to document the methods as optional-to-implement, though, beyond just requiring a protocol extension. My ideal option would be to allow the optional keyword and change it to simply require a default implementation in a protocol extension.

If we don't want a language change, then perhaps a conventional doc tag?

A.

On Mar 30, 2016, at 8:08 PM, Yuval Tal via swift-evolution <swift-evolution@swift.org> wrote:

Hi,

I find that optional protocol methods to be very useful. However, there is a caveat -- it needs to be mapped to @objc. This puts a set of limitations, such as: structures cannot be used as parameters as it does not map to objective-c. What do you think about removing the requirement of using @objc and allow to create optional methods without these limitations?

Thank you,

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

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

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


(Andrey Tarantsov) #4

Some ideas I was thinking about for optional protocol functions was you’d declare it like this:

protocol UIGestureRecognizerDelegate {
optional func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool = true
}

If you put “optional” there, the compiler would auto-generate an empty function in a protocol extension in your module as if you had specified one yourself. If the function has a return value, you would be required to specify the default return value in the protocol which provides automatic documentation of the default, too.

REALLY digging this. Strong +1.

A.


(Rob Mayoff) #5

UIGestureRecognizerDelegate has five methods that are "named"
gestureRecognizer:

   - gestureRecognizer(_:shouldRecognizeSimultaneouslyWithGestureRecognizer:
   )
   - gestureRecognizer(_:shouldRequireFailureOfGestureRecognizer:)
   - gestureRecognizer(_:shouldBeRequiredToFailByGestureRecognizer:)
   - gestureRecognizer(_:shouldReceiveTouch:)
   - gestureRecognizer(_:shouldReceivePress:)

You can only have a single property named "gestureRecognizer", so you
either have to come up with other names for these, or change the language
to allow closure-typed properties to have multipart names.

This problem has been noted before, for example here:
http://article.gmane.org/gmane.comp.lang.swift.evolution/8707/

···

On Thu, Mar 31, 2016 at 10:56 AM, Thorsten Seitz via swift-evolution < swift-evolution@swift.org> wrote:

protocol UIGestureRecognizerDelegate {
    var gestureRecognizerShouldBegin: ((gestureRecognizer:
UIGestureRecognizer) -> Bool)? { get }
}


(Thorsten Seitz) #6

Good point. That would obviously restrict the choice for naming the methods in such a protocol, but since we are talking about new protocols that would not be an impediment. It might result in non-optimal method names, of course.

-Thorsten

···

Am 31.03.2016 um 18:37 schrieb Rob Mayoff via swift-evolution <swift-evolution@swift.org>:

On Thu, Mar 31, 2016 at 10:56 AM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org> wrote:

protocol UIGestureRecognizerDelegate {
    var gestureRecognizerShouldBegin: ((gestureRecognizer: UIGestureRecognizer) -> Bool)? { get }
}

UIGestureRecognizerDelegate has five methods that are "named" gestureRecognizer:

gestureRecognizer(_:shouldRecognizeSimultaneouslyWithGestureRecognizer:)
gestureRecognizer(_:shouldRequireFailureOfGestureRecognizer:)
gestureRecognizer(_:shouldBeRequiredToFailByGestureRecognizer:)
gestureRecognizer(_:shouldReceiveTouch:)
gestureRecognizer(_:shouldReceivePress:)
You can only have a single property named "gestureRecognizer", so you either have to come up with other names for these, or change the language to allow closure-typed properties to have multipart names.

This problem has been noted before, for example here: http://article.gmane.org/gmane.comp.lang.swift.evolution/8707/

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


(Lukas Stabe) #7

All use-cases I had for optional methods in protocols in Objective-C are covered nicely by providing a default implementation in a protocol extension, so I don't think optional protocol methods should be a thing in pure Swift.

I do not understand why an optional method should require a default value. That’s not how optional methods work in Objective-C where the caller will ask whether the method is implemented and calls it or does something else. With a default value it would be much more difficult to do something different if the method is not implemented.
Actually with a default value the method would just be a normal method which has not been overridden, there would be nothing optional about it anymore.

I've actually had multiple cases in Objective-C code where this feature (some object behaving differently depending on wether or not a delegate method has been implemented) has prevented me from implementing features the easy and obvious way. In those cases I resorted to implementing 'respondsToSelector:'. So I'd argue that optional protocol methods encourage this type of behavior, which imho is bad API design. If you need to behave differently for some types of delegates (or whatever else your protocol represents), a separate method to call to determine how to behave is much simpler and better to use.

– Lukas


(Andrey Tarantsov) #8

I do not understand why an optional method should require a default value. That’s not how optional methods work in Objective-C where the caller will ask whether the method is implemented and calls it or does something else. With a default value it would be much more difficult to do something different if the method is not implemented.

Yes, and that's the best part. The way Obj-C optional methods currently work, they are very hard to wrap/proxy/etc. Swift has a lot more expressive power, so I'm sure that you can adjust the return value to express the “I don't care” case without making the absence of the method magical.

A.


(Matthew Johnson) #9

All use-cases I had for optional methods in protocols in Objective-C are covered nicely by providing a default implementation in a protocol extension, so I don't think optional protocol methods should be a thing in pure Swift.

I do not understand why an optional method should require a default value. That’s not how optional methods work in Objective-C where the caller will ask whether the method is implemented and calls it or does something else. With a default value it would be much more difficult to do something different if the method is not implemented.
Actually with a default value the method would just be a normal method which has not been overridden, there would be nothing optional about it anymore.

I've actually had multiple cases in Objective-C code where this feature (some object behaving differently depending on wether or not a delegate method has been implemented) has prevented me from implementing features the easy and obvious way. In those cases I resorted to implementing 'respondsToSelector:'. So I'd argue that optional protocol methods encourage this type of behavior, which imho is bad API design. If you need to behave differently for some types of delegates (or whatever else your protocol represents), a separate method to call to determine how to behave is much simpler and better to use.

+1. This is exactly the same thing I mentioned regarding the fast path for row height in UITableView. The fact that it depends on whether or not the delegate implements heightForRowAtIndexPath is a bad design decision.

···

Sent from my iPad

On Apr 2, 2016, at 7:04 AM, Lukas Stabe via swift-evolution <swift-evolution@swift.org> wrote:

– Lukas

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


(Andrey Tarantsov) #10

I've actually had multiple cases in Objective-C code where this feature (some object behaving differently depending on wether or not a delegate method has been implemented) has prevented me from implementing features the easy and obvious way. In those cases I resorted to implementing 'respondsToSelector:'. So I'd argue that optional protocol methods encourage this type of behavior, which imho is bad API design. If you need to behave differently for some types of delegates (or whatever else your protocol represents), a separate method to call to determine how to behave is much simpler and better to use.

+1. This is exactly the same thing I mentioned regarding the fast path for row height in UITableView. The fact that it depends on whether or not the delegate implements heightForRowAtIndexPath is a bad design decision.

Yes, but please observe that the form of optional methods that we're discussing here is limited to avoid the case you mention (I hate that problem as well, very very much).

A.