[swift-evolution] Type-safe selectors

Michel Fortin michel.fortin at michelf.ca
Fri Dec 4 16:22:45 CST 2015


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 

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.

 - - -

This is inspired from the D/Objective-C compiler I prototyped a while ago. There, I made selectors typed with their arguments because:

1. I wanted to preserve D's type-safety while still being able to use selectors, and
2. I needed to decouple selector names from method names in the code; this would later allow me to implement overloading by adding some name mangling in selectors. Obtaining selectors by referring to the method allowed selectors to become an implementation detail while providing a nice way to associate the parameter types.

For reference, here is the meager documentation for that feature:
https://michelf.ca/projects/d-objc/syntax/#selector-literals
And for the selector-name mangling:
https://michelf.ca/projects/d-objc/syntax/#generated-selectors


-- 
Michel Fortin
michel.fortin at michelf.ca
https://michelf.ca



More information about the swift-evolution mailing list