So I've actually been doing a lot of thinking about this, because I have a use case that doesn't work as well as in Swift.
You have some problems. The first problem you have, is that static compilation provides so many routes for cross-applicaton optimization that this is one of the major ways that Swift can be faster than its predecessors. For example, if you have this
func min<T: Comparable>(x: T, y: T) -> T { //Generic functions
if x < y {
return y
return x
}
min(3,5)
min(5.0,2.0)
The compiler has a lot of possibilities. For example it could translate this function very literally, producing the generic min function that you wrote. It could realize that you only use this for floats and ints, and emit 2 particular specializations of the min function. It could specialize the float function and use the generic function for ints, or vice versa. Next, it could inline any of these, none of them, or some of them. Finally it could do three different things depending on if you're x86-64, armv7s, or arm64.
I have no idea what the right thing to do is in this situation, but having all those possibilities gives the compiler a better chance of producing faster code. When you introduce message passing, you basically forbid all of those optimizations, because now the function might be swizzled, go pass a "minX:Y:" string to objcMsgSend to find out what pointer we should jump to today.
Swift already does have a way to opt-in to message passing, it's called the @objc flag (and performSelector:). It works fine, but it's extra work to opt into it and use it. But, I don't think that's bad given that it disables so many useful optimizations.
What may be bad is that the name "@objc" is a dead giveaway that this is a feature with a deprecation date. So I think we need some other, reflectionish system going forward.
For mocks one idea would be to selectively turn on message-passing as the dispatch method at the compiler level. So you could compile your application for unit testing with a "-fmessage-passing" like we have "-fobjc-no-arc" and get all the dynamism in debug builds and turn them off for release builds (or not, as the case may be).
For everyday power programming I think the answer is a lot simpler: functions are first-class objects in Swift, so just take your functions, put them in a dictionary somewhere and implement your own run-time dispatch. Having done this, I can say with certainty that there are some ways the language could improve this use case but it is possible, and if you are Chris Espinosa wrestling a bit with the type system is not a problem.
Things are tricky when you don't have the source code, but here again I see a reflection-ish API as being the key. I expect at some point we will be able to enumerate other classes' public methods, and dynamically dispatch into them, and that is certainly a missing feature with valid use cases.
Finally, I fundamentally and strongly disagree with Michael about method swizzling and OSX extensibility. Adding PGP to Mail via hooking functions sounds like an incredibly deranged idea. The suggestion that programs should cooperate with a badly-misguided reverse engineering effort by disabling optimizations that make the program faster for the other 99% is just insane. The way to approach this is to build new kinds of extensions that provide known, safe, documented extension entrypoints for hooking arbitrary code. Yes that is more work, and yes it requires actually talking with people and convincing them of your point of view, but so do most things.
> I have no idea what the right thing to do is in this situation, but having all those possibilities gives the compiler a better chance of producing faster code. When you introduce message passing, you basically forbid all of those optimizations, because now the function might be swizzled, go pass a "minX:Y:" string to objcMsgSend to find out what pointer we should jump to today.
> Swift already does have a way to opt-in to message passing, it's called the @objc flag (and performSelector:). It works fine, but it's extra work to opt into it and use it. But, I don't think that's bad given that it disables so many useful optimizations.
You could keep some of these optimizations if Swift's message-passing mechanism preserved type information. This would make it slower than objc_msgSend, but you could still keep a lot of cool optimizations. Some optimizations would still not be possible, and extensive use of function pointers does tend to frustrate branch prediction, but I feel that for most apps, this would be fast enough.
> Finally, I fundamentally and strongly disagree with Michael about method swizzling and OSX extensibility. Adding PGP to Mail via hooking functions sounds like an incredibly deranged idea. The suggestion that programs should cooperate with a badly-misguided reverse engineering effort by disabling optimizations that make the program faster for the other 99% is just insane. The way to approach this is to build new kinds of extensions that provide known, safe, documented extension entrypoints for hooking arbitrary code. Yes that is more work, and yes it requires actually talking with people and convincing them of your point of view, but so do most things.
I think this is a great argument, but unfortunately it's not where OS X is today. As I mentioned, app extensions may be a step towards a more workable solution, but today, the ability to add PGP to mail using method swizzling is important to thousands of Mail.app users. If 10.11 comes out and Mail.app has been rewritten in Swift before we have a good alternative to method swizzling, we'll have lost a lot of tools that make the Mac a powerful platform.
> You could keep some of these optimizations if Swift's message-passing mechanism preserved type information.
Type information isn't what makes this fast. What makes it fast is the very guarantee that dynamic dispatch breaks: knowing, for a certainty, where the function will land.
> As I mentioned, app extensions may be a step towards a more workable solution, but today, the ability to add PGP to mail using method swizzling is important to thousands of Mail.app users.
A fast Mail app is important to millions of Mail users.
If PGP is important then one interim possibility is to write your own mail client. That requires extra work, but one of the hallmarks of important problems is that we are willing to do extra work to solve them.
You have some problems. The first problem you have, is that static compilation provides so many routes for cross-applicaton optimization that this is one of the major ways that Swift can be faster than its predecessors. For example, if you have this
The compiler has a lot of possibilities. For example it could translate this function very literally, producing the generic min function that you wrote. It could realize that you only use this for floats and ints, and emit 2 particular specializations of the min function. It could specialize the float function and use the generic function for ints, or vice versa. Next, it could inline any of these, none of them, or some of them. Finally it could do three different things depending on if you're x86-64, armv7s, or arm64.I have no idea what the right thing to do is in this situation, but having all those possibilities gives the compiler a better chance of producing faster code. When you introduce message passing, you basically forbid all of those optimizations, because now the function might be swizzled, go pass a "minX:Y:" string to objcMsgSend to find out what pointer we should jump to today.
Swift already does have a way to opt-in to message passing, it's called the @objc flag (and performSelector:). It works fine, but it's extra work to opt into it and use it. But, I don't think that's bad given that it disables so many useful optimizations.
What may be bad is that the name "@objc" is a dead giveaway that this is a feature with a deprecation date. So I think we need some other, reflectionish system going forward.
For mocks one idea would be to selectively turn on message-passing as the dispatch method at the compiler level. So you could compile your application for unit testing with a "-fmessage-passing" like we have "-fobjc-no-arc" and get all the dynamism in debug builds and turn them off for release builds (or not, as the case may be).
For everyday power programming I think the answer is a lot simpler: functions are first-class objects in Swift, so just take your functions, put them in a dictionary somewhere and implement your own run-time dispatch. Having done this, I can say with certainty that there are some ways the language could improve this use case but it is possible, and if you are Chris Espinosa wrestling a bit with the type system is not a problem.
Things are tricky when you don't have the source code, but here again I see a reflection-ish API as being the key. I expect at some point we will be able to enumerate other classes' public methods, and dynamically dispatch into them, and that is certainly a missing feature with valid use cases.
Finally, I fundamentally and strongly disagree with Michael about method swizzling and OSX extensibility. Adding PGP to Mail via hooking functions sounds like an incredibly deranged idea. The suggestion that programs should cooperate with a badly-misguided reverse engineering effort by disabling optimizations that make the program faster for the other 99% is just insane. The way to approach this is to build new kinds of extensions that provide known, safe, documented extension entrypoints for hooking arbitrary code. Yes that is more work, and yes it requires actually talking with people and convincing them of your point of view, but so do most things.