Type-safe selectors

Like this:

  view.action = "_doSomething_UniqueSelector1234_currentModuleName_blahblah"

  extension NSObject {
    func _doSomething_UniqueSelector1234_currentModuleName_blahblah() {
      let target = self
      (target as! MyObject).doSomething()
    }
  }

(Joe Groff suggested it first.) This simply assumes the receiver will
derive from NSObject. You also need to set a non-nil target. And note
that the closure is context-free, meaning you can't capture variables
with it.

How is this type-safe? You're using as! in there (and silently ignoring
the message if the receiver is wrong wouldn't be any better). If I send
a selector to the wrong receiver, it's still going to crash.

I think this would be better emitted like a category on MyObject than on NSObject.

This comes back to my fundamental argument, which is that
@convention(selector) looks like it's type-safe, but there's so many
holes and so many implicit coercions that need to be added that you can
drive a gigantic truck full of unsafety right through it without even
noticing.

There's plenty of useful space between "no type safety" and "perfect type safety", IMO.

-Joe

···

On Dec 5, 2015, at 12:35 PM, Kevin Ballard <kevin@sb.org> wrote:
On Sat, Dec 5, 2015, at 04:30 AM, Michel Fortin wrote:

> How is this type-safe? You're using as! in there (and silently ignoring
> the message if the receiver is wrong wouldn't be any better). If I send
> a selector to the wrong receiver, it's still going to crash.

I think this would be better emitted like a category on MyObject than on
NSObject.

Going back and reading your original suggestion about generating
closures, I think we're talking about this differently. If I understand
it right, your original suggestion for generating closures basically
just solves the problem of forgetting to mark a method as @objc, right?
In that case, putting the category on MyObject instead of NSObject is
absolutely the right thing to do. My arguments here are actually about
Michel Fortin's claim:

The fact that the selector lives separately from its target object makes things difficult because the expected target type is almost always going to be AnyObject. Implicit conversions cannot happen safely in the direction SubType to BaseType for the arguments, including the target object. That makes Joe Groff's approach the only type-safe solution: make an extension of the base object and generate a method that does what you want.

Which is in the context of the fact that @convention(selector) MyObject
-> Args -> Ret encodes the receiver type, but the problem is the
selector is sent to AnyObject, so having the receiver type in there is
basically false type-safety (it looks like it's strongly-typed but it's
actually not as that type information will be thrown away before the
selector is used). I'm not really sure why Michel claimed that your
closure suggestion was a solution to this problem, because the only
reasonable behavior one can use in such a category emitted on
AnyObject/swift base class is to fatalError, which is no better than
what happens if the selector simply isn't implemented.

Ultimately, given the premise that @convention(selector) T -> Args ->
Ret exists, generating those methods on the receiver type with mangled
selector names seems like reasonable behavior. I just don't think
@convention(selector) should exist.

There's plenty of useful space between "no type safety" and "perfect type safety", IMO.

In principle I agree. My basic argument here is that
@convention(selector) provides so little actual type safety that it's
not worth the language complexity. And I also worry that providing
functionality that appears to be strongly-typed but doesn't actually
provide any typing guarantees in practice will end up as a safety hazard
for unwary programmers.

It's also worth pointing out that my alternative suggestion of simply
providing some syntax to explicitly get a Selector from a method could
still use your closure-generating idea to work for non-@objc methods.
Heck, we don't even really need syntax for this, we could just say that
unbound method references can resolve to Selectors in addition to
function types (just as the primary suggestion here allows method
references to resolve to @convention(selector) functions in addition to
@convention(swift) functions).

-Kevin Ballard

···

On Sat, Dec 5, 2015, at 06:00 PM, Joe Groff wrote:

Which is in the context of the fact that @convention(selector) MyObject
-> Args -> Ret encodes the receiver type, but the problem is the
selector is sent to AnyObject, so having the receiver type in there is
basically false type-safety (it looks like it's strongly-typed but it's
actually not as that type information will be thrown away before the
selector is used). I'm not really sure why Michel claimed that your
closure suggestion was a solution to this problem, because the only
reasonable behavior one can use in such a category emitted on
AnyObject/swift base class is to fatalError, which is no better than
what happens if the selector simply isn't implemented.

Somehow in the thinking process I forgot that the category could have been put on MyObject instead of NSObject. I agree it's better to set the category on MyObject.

As for why a category at all? The risk is that another class implements "doSomething:" with a different argument type or return type than the "doSomething:" referring to, and that somehow an object of this other class becomes the target. In that case you want to halt the program to avoid the other "doSomething:" method from being called. That's why I suggested using a unique selector. And that idea came from Joe's suggestion of creating unique selectors from context-free closures.

There's plenty of useful space between "no type safety" and "perfect type safety", IMO.

In principle I agree. My basic argument here is that
@convention(selector) provides so little actual type safety that it's
not worth the language complexity. And I also worry that providing
functionality that appears to be strongly-typed but doesn't actually
provide any typing guarantees in practice will end up as a safety hazard
for unwary programmers.

This is a reasonable concern. My opinion is that for @convention(selector) to be worthwhile, it needs to be safe.. If it can't be made safe, then maybe the idea probably should be abandoned. I think we are on the same page here, except that I'm more optimistic about type-safety.

It's also worth pointing out that my alternative suggestion of simply
providing some syntax to explicitly get a Selector from a method could
still use your closure-generating idea to work for non-@objc methods.
Heck, we don't even really need syntax for this, we could just say that
unbound method references can resolve to Selectors in addition to
function types (just as the primary suggestion here allows method
references to resolve to @convention(selector) functions in addition to
@convention(swift) functions).

Most ideas floating in this thread are actually not mutually exclusive. I wouldn't qualify them as "alternatives" really since they can easily combine. We should look at the tradeoff of each and keep the good ones.

···

Le 5 déc. 2015 à 22:35, Kevin Ballard <kevin@sb.org> a écrit :

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