[swift-evolution] Type-safe selectors

Joe Groff jgroff at apple.com
Fri Dec 4 16:31:59 CST 2015


> On Dec 4, 2015, at 2:26 PM, Joe Groff <jgroff at apple.com> wrote:
> 
>> 
>> On Dec 4, 2015, at 2:22 PM, Michel Fortin <michel.fortin at michelf.ca> wrote:
>> 
>> Currently in Swift you can get a closure by referring to a method:
>> 
>> 	let x = NSString.hasPrefix
>> 	// x is of type NSString -> String -> Bool
>> 
>> Something that would be useful here is if the closure created from Objective-C methods were special in that they could implicitly be converted to a Selector. Instead of writing manually a selector as a string, you'd just have to refer to the method, and you know there's no typo (or else you get a compile-time error). For instance, adding an observer to a NSNotificationCenter would work like this:
>> 
>> 	notificationCenter.addObserver(self, selector: MyClass.observeNotification, name: NSSomeNotificationName, object: nil) 
>> 
>> This, making sure the correct selector is used for the designated method, seem like it should be somewhat more important in Swift 3 if it includes Evolution Proposal 0005 that suggests many Objective-C methods will be given Swift-specific names.
>> https://github.com/apple/swift-evolution/blob/master/proposals/0005-objective-c-name-translation.md
>> 
>> But why stop there when you can go one step further and actually improve type-safety? Instead of taking a Selector parameter, the NSNotificationCenter.addObserver method above could request a @convention(selector) closure of this form:
>> 
>> 	@convention(selector) AnyObject -> NSNotification -> Void
>> 
>> Under the hood that closure is still a plain selector pointer, but the compiler attaches proper type information to the arguments. Since `addObserver` now declares it wants a selector with the given signature, the compiler can enforce that the arguments and return type for the passed selector are compatible. You 
> 
> This is a great approach, and it's mostly exactly what I've had in mind for this. Another nice thing about @convention(selector) is that the compiler could also context-free closures, like with @convention(c), by compiling them down to categories with mangled methods.
> 
>> 
>> Moreover, the @convention(selector) closure you get can then be used as a normal closure inside a Swift method that can be called from Objective-C:
>> 
>> 	@objc func callSelector(selector: @convention(selector) NSString -> String -> Bool) -> Bool {
>> 		let str = NSString(string: "hello")
>> 		return selector(str)("hell")
>> 	}
>> 
>> 	let x = NSString.hasPrefix
>> 	// x is of type @convention(selector) NSString -> String -> Bool
>> 	callSelector(x)
>> 
>> So that would make selectors less error-prone because the compiler can type-check them, and you can use selectors in Swift code in a very natural manner.
> 
> I would say that 'let x = NSString.hasPrefix' should still give you a @convention(swift) function value by default; that's what we do with method references in general, but you could ask for a @convention(selector) reference explicitly:
> 
> let x: @convention(selector) X -> Y -> Z = X.hasPrefix

One wrinkle you need to consider is the different ARC behavior of method families. @convention(selector) would have to be restricted to referencing methods that don't belong to an usual method family or have unusual ownership rules, or else we'd need multiple @convention(init_selector), (copy_selector), (alloc_selector) etc. conventions.

-Joe
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151204/82063808/attachment-0001.html>


More information about the swift-evolution mailing list