Swift's object syntax not the clearest or most efficient

Hi


I have watched the WWDC talk 409 on optimizing performance. There is a considerable overhead in having dymanic methods that can be overridden. Using "final" methods is much more efficient. But of course if you are desiging a set of classes that need to override methods, that is part of the design.


I think it would be much better for Swift methods to be final by default, and to require a keyword if they are to be overridden. In general, a method is not meant to be overridden. In fact, leaving all methods available for overriding seems dangerous.


At least one other object-oriented language, Delphi, works this way: methods are final by default and must be marked virtual to be overridden. This seems eminiently sensible. Overriding methods seems best if it is designed in from the start, and safer. And that has performance benefits.


Regards

Mark

I agree that the final should be implicit, but only until override is used in a subclass. Is this not the way the compiler works now? The virtual keyword, as far as I can tell, is unneeded clutter. (I don't know Delphi, but I'm very familiar with virtual, and sealed –the equivalent of final– in C#.) If the final is implicit, then override can be used. If final is explicit, then override cannot be used. Are there any potential problems with that system?

considerable overhead


Honest what is considerable overhead at a single indirect jump?

I think the current syntax using final and override are fine. The reason is as follows. If you've made the decision to define a new class, most likely it is because, you are designing for inheritence or you are inheriting another class you need which may or may not be from Objective-C which has no concept of final classes. So, most likely you are using classes because you have made a decision that you need or want to embrace inheritance. With that decision comes the responsibility to manage inheritance which means deciding what is final and what is accessible.


By default, classes, methods and properties have internal access. With whole module optimization, you get much of the benefit that you would get by marking members final when using internal access which is the default. Where things matter most from a safety and performance perspective is when you choose to make a class or member public. In doing so, you must take responsibility to decide whether to mark it final or not.


I think the default access levels along with the decision to adopt classes and having to mark methods with override are sufficient protections.

You said, "final should be implicit, but only until override is used in a subclass." I don't see how this can be so. When you mark a member final, you are declaring that it should not be overriden and any attempts to do so should be prevented. You may mark something public final to allow it to be used outside your module but you don't want any side effects that may happen if it were to be overriden.

You're assuming I'm talking to the compiler when I say final, but I'm actually talking to whoever's using my code. As such, if I mark something final, you know it, and the compiler knows it. If I don't, the compiler knows it, until you override it.

I think I see what you are saying. Yes, if you don't mark a member final, then with whole module optimization enabled the compiler can determine whether or not an internal member needs dynamic dispatch based on whether it was overriden within the module.

That's better than nothing, but not as good as it should be. It seems to me that Bitcode ought to be able to be leveraged for multi-module optimization.

Well, if you mark a member public and not final then you are making it available for use and possibly overriden outside your module. The compiler can't determine whether a member will be overriden by some other module in potentially some other project that you don't even control.

Why not? The time to do that compiling is not when you make the module. The time to do it is when the final project is built.

When you deliver a framework it is in binary form and can be loaded dynamically into the final application, so the framework must already be fully compiled.

I'm talking about should. The way things are is not the way they would be if they were best, so as development time is available, it should be changed to the more flexible and performant option.

Through what mechanism? Are you suggesting that dynamic libraries not be fully compiled? Then you would be taking a performance hit at runtime when you load the dynamic library.

The unknowable portions of dynamic libraries would be compiled after the module is published for consumption, and as the project is built/archived.

Everything in engineering is a trade-off, and in this case, I think the Swift team has made the correct tradeoff. It's true that by default, methods that are never overridden are slightly less performant than if they had been marked final, but as others have pointed out, this is mitigated by whole-module optimization, and Swift is already performant enough for most applications. Many apps will never use the final keyword and still provide great performance, both in speed and power usage, to users.


There are also downsides to final by default. Most methods will be designed to be overridden, or at least designed with the assumption that they can be overridden. Forcing developers to mark these methods as non-final would create a lot of extra, rote work for developers. I see this all the time in C++ code, where developers have to painstakingly mark nearly every method on their class as virtual. In C++, non-virtual methods can still be overridden, so it's not a perfect analogy, but non-virtual methods are not available to dynamic dispatch, so it's close enough. C++ makes this decision because one of its driving philosophies is to prioritize speed of execution over other considerations. It's a legitimate philosophy, but designing for developer speed is also a legitimate philosophy.


Final by default would also end up creating a lot of bugs with third-party frameworks. For these frameworks, final methods are even more the exception than the rule. Not all methods are designed for the user to override, but rather designed so that they can be overridden if needed. If a framework author forgets to mark a method as non-final, because they don't override it themselves, users may not be able to use a framework until that oversight is corrected.

It seems you have a different approach to programming from me. You said "Most methods will be designed to be overridden, or at least designed with the assumption that they can be overridden." But for me I think carefully about which methods should be overridden, and try to make them as few in number as I can. That communicates how the class is being designed, and allows the programmer to make assumptions about the methods not intended to be overridden.

Swift's object syntax not the clearest or most efficient
 
 
Q