Final by default for classes and methods


(Javier Soto) #1

This was brought up in a different thread about private by default.
Creating a new thread for this. Quoting Mathew from the other thread with a
short summary about the motivation behind this:

"It is not uncommon to have a need for a reference type without a need for
inheritance. Superclasses should be intentionally designed to be
subclasses and the author required to opt-in to subclassing and member
overrides where that is required by the design."

···

--
Javier Soto


(Slava Pestov) #2

Another idea we've discussed internally is some form of 'sealed' classes. That is, classes that are publicly visible but only subclassable from within their module. This could be used to define closed inheritance hierarchies which give stronger guarantees to the optimizer.

I'd personally be in favor of making that behavior the default.

Slava

···

On Dec 7, 2015, at 11:12 AM, Javier Soto via swift-evolution <swift-evolution@swift.org> wrote:

This was brought up in a different thread about private by default. Creating a new thread for this. Quoting Mathew from the other thread with a short summary about the motivation behind this:

"It is not uncommon to have a need for a reference type without a need for inheritance. Superclasses should be intentionally designed to be subclasses and the author required to opt-in to subclassing and member overrides where that is required by the design."
--
Javier Soto _______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Joe Groff) #3

There's a refinement of this idea we've discussed internally in the past. Instead of making classes final by default, we could make it so that public classes are not subclassable from other modules by default. Inheritance is manageable within a module, where all of the involved subclasses are revlocked and potentially have access to each other's implementations anyway, and the benefits of `final` and closed class hierarchy analysis are easy to recover with whole module analysis so don't necessarily need explicit calling out for internal interfaces. The problems with inheritance rear their head more with public interfaces, once code outside your control can subclass your classes.

-Joe

···

On Dec 7, 2015, at 11:12 AM, Javier Soto via swift-evolution <swift-evolution@swift.org> wrote:

This was brought up in a different thread about private by default. Creating a new thread for this. Quoting Mathew from the other thread with a short summary about the motivation behind this:

"It is not uncommon to have a need for a reference type without a need for inheritance. Superclasses should be intentionally designed to be subclasses and the author required to opt-in to subclassing and member overrides where that is required by the design."


(Riley Testut) #4

Fully agree with Kevin here. I would really hate to see final as the default for classes/methods. In addition to subclassing just to add custom behavior, everyone writes bugs, and sometimes (unfortunately) the clients of an API have no choice to fix the bugs besides subclassing and fixing them themselves. Additionally, it would be significantly harder (IMO) for newcomers to grasp the concept when subclassing is limited to certain classes but not others. If you actually need that performance gain, you can easily add the final yourself.

It is true that “it is not uncommon to have a need for a reference type without a need for inheritance”. However, that does not mean this should be the default, IMO. So while I agree in theory it would be nice, in practice I don’t want to prevent potential clients of Swift APIs from accomplishing their goals in the easiest way possible.

···

On Dec 7, 2015, at 11:21 AM, Kevin Lundberg via swift-evolution <swift-evolution@swift.org> wrote:

Resending to whole list:

I can appreciate the desire for this, but I would not support it personally. In a former life developing C# code where this is the default behavior, I wanted to extend a class's behavior to make it work slightly differently, but since everything is final by default (sealed in C# parlance) I was prevented from doing so. The scope of my change was minimal and well defined, and it would not have negatively impacted the extending class's normal behavior. The code I was extending was open source so i ended up having to copy and paste the class and its dependencies into my project to do what I wanted. If I couldn't have done that then I'm not sure what the right approach would have been.

In short, it is not always possible to know ahead of time how your clients will want to extend your API's functionality; artificially limiting this in my opinion will make it much harder to reuse components in previously unforeseen ways.

--
Kevin Lundberg

On Dec 7, 2015, at 2:12 PM, Javier Soto via swift-evolution <swift-evolution@swift.org> wrote:

This was brought up in a different thread about private by default. Creating a new thread for this. Quoting Mathew from the other thread with a short summary about the motivation behind this:

"It is not uncommon to have a need for a reference type without a need for inheritance. Superclasses should be intentionally designed to be subclasses and the author required to opt-in to subclassing and member overrides where that is required by the design."
--
Javier Soto
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Owens II) #5

The problem is that this perpetuates the fundamental problem with type hierarchies: the further down the rabbit hole you go, the more fragile your types end up being.

I’d much rather design for inheritance rather than having it be opt-in by-default. I really like the inheritable only from within the module by default idea that Joe and Slava mentioned.

···

On Dec 7, 2015, at 11:21 AM, Kevin Lundberg via swift-evolution <swift-evolution@swift.org> wrote:

Resending to whole list:

I can appreciate the desire for this, but I would not support it personally. In a former life developing C# code where this is the default behavior, I wanted to extend a class's behavior to make it work slightly differently, but since everything is final by default (sealed in C# parlance) I was prevented from doing so. The scope of my change was minimal and well defined, and it would not have negatively impacted the extending class's normal behavior. The code I was extending was open source so i ended up having to copy and paste the class and its dependencies into my project to do what I wanted. If I couldn't have done that then I'm not sure what the right approach would have been.

In short, it is not always possible to know ahead of time how your clients will want to extend your API's functionality; artificially limiting this in my opinion will make it much harder to reuse components in previously unforeseen ways.

--
Kevin Lundberg

On Dec 7, 2015, at 2:12 PM, Javier Soto via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

This was brought up in a different thread about private by default. Creating a new thread for this. Quoting Mathew from the other thread with a short summary about the motivation behind this:

"It is not uncommon to have a need for a reference type without a need for inheritance. Superclasses should be intentionally designed to be subclasses and the author required to opt-in to subclassing and member overrides where that is required by the design."
--
Javier Soto
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Riley Testut) #6

Fully agree with Kevin here. I would really hate to see final as the default for classes/methods. In addition to subclassing just to add custom behavior, everyone writes bugs, and sometimes (unfortunately) the clients of an API have no choice to fix the bugs besides subclassing and fixing them themselves. Additionally, it would be significantly harder (IMO) for newcomers to grasp the concept when subclassing is limited to certain classes but not others. If you actually need that performance gain, you can easily add the final yourself.

It is true that “it is not uncommon to have a need for a reference type without a need for inheritance”. However, that does not mean this should be the default, IMO. So while I agree in theory it would be nice, in practice I don’t want to prevent potential clients of Swift APIs from accomplishing their goals in the easiest way possible.

···

On Dec 7, 2015, at 11:30 AM, David Owens II via swift-evolution <swift-evolution@swift.org> wrote:

The problem is that this perpetuates the fundamental problem with type hierarchies: the further down the rabbit hole you go, the more fragile your types end up being.

I’d much rather design for inheritance rather than having it be opt-in by-default. I really like the inheritable only from within the module by default idea that Joe and Slava mentioned.

On Dec 7, 2015, at 11:21 AM, Kevin Lundberg via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Resending to whole list:

I can appreciate the desire for this, but I would not support it personally. In a former life developing C# code where this is the default behavior, I wanted to extend a class's behavior to make it work slightly differently, but since everything is final by default (sealed in C# parlance) I was prevented from doing so. The scope of my change was minimal and well defined, and it would not have negatively impacted the extending class's normal behavior. The code I was extending was open source so i ended up having to copy and paste the class and its dependencies into my project to do what I wanted. If I couldn't have done that then I'm not sure what the right approach would have been.

In short, it is not always possible to know ahead of time how your clients will want to extend your API's functionality; artificially limiting this in my opinion will make it much harder to reuse components in previously unforeseen ways.

--
Kevin Lundberg

On Dec 7, 2015, at 2:12 PM, Javier Soto via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

This was brought up in a different thread about private by default. Creating a new thread for this. Quoting Mathew from the other thread with a short summary about the motivation behind this:

"It is not uncommon to have a need for a reference type without a need for inheritance. Superclasses should be intentionally designed to be subclasses and the author required to opt-in to subclassing and member overrides where that is required by the design."
--
Javier Soto
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #7

There's a refinement of this idea we've discussed internally in the past. Instead of making classes final by default, we could make it so that public classes are not subclassable from other modules by default.

This would be much better than current behavior, but I still favor final by default. I really believe inheritance and overridability is part of an API contract that should be explicitly stated in code even for code within a module. This enhances readability, encourages thoughtful design, and prevents unintentional interfaces.

There is one really interesting aspect of this idea though. It would allow public classes to be subclasses internally, but not outside the module. Restricting the ability to inherit and / or override to a specific access control scope might be extremely useful.

I think we could have both - final by default as well as access restricted inheritance and overriding.

···

Sent from my iPad

On Dec 7, 2015, at 1:19 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 7, 2015, at 11:12 AM, Javier Soto via swift-evolution <swift-evolution@swift.org> wrote:

This was brought up in a different thread about private by default. Creating a new thread for this. Quoting Mathew from the other thread with a short summary about the motivation behind this:

"It is not uncommon to have a need for a reference type without a need for inheritance. Superclasses should be intentionally designed to be subclasses and the author required to opt-in to subclassing and member overrides where that is required by the design."

There's a refinement of this idea we've discussed internally in the past. Instead of making classes final by default, we could make it so that public classes are not subclassable from other modules by default. Inheritance is manageable within a module, where all of the involved subclasses are revlocked and potentially have access to each other's implementations anyway, and the benefits of `final` and closed class hierarchy analysis are easy to recover with whole module analysis so don't necessarily need explicit calling out for internal interfaces. The problems with inheritance rear their head more with public interfaces, once code outside your control can subclass your classes.

-Joe

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #8

The problem is that this perpetuates the fundamental problem with type hierarchies: the further down the rabbit hole you go, the more fragile your types end up being.

+1


(Sean Heber) #9

This would be much better than current behavior, but I still favor final by default. I really believe inheritance and overridability is part of an API contract that should be explicitly stated in code even for code within a module. This enhances readability, encourages thoughtful design, and prevents unintentional interfaces.

It is almost like there’s potential for a third fundamental “type." We have “struct” and “class” and they represent “value” and “reference", but often what one simply wants or needs is, more or less, just a pointer to a struct. If a 3rd type existed, classes could be reserved for “this is for subclassing, you must override methods, call super, etc”, and value types/structs remain as they are with their copying semantics, etc. and the third type would be, essentially, a sealed/final kind of class - but with it’s own distinct name and rules rather than being considered a “class" in the usual sense.

struct Foo {
// value type, no subclassing
}

class Bar {
// reference type, must considering subclassing (enforced calling super, etc?)
}

handle Baz {
// reference type, no subclassing
}

I haven’t thought about this a lot - maybe it’s silly. :slight_smile:

l8r
Sean


(Kevin Lundberg) #10

Resending to whole list:

I can appreciate the desire for this, but I would not support it personally. In a former life developing C# code where this is the default behavior, I wanted to extend a class's behavior to make it work slightly differently, but since everything is final by default (sealed in C# parlance) I was prevented from doing so. The scope of my change was minimal and well defined, and it would not have negatively impacted the extending class's normal behavior. The code I was extending was open source so i ended up having to copy and paste the class and its dependencies into my project to do what I wanted. If I couldn't have done that then I'm not sure what the right approach would have been.

In short, it is not always possible to know ahead of time how your clients will want to extend your API's functionality; artificially limiting this in my opinion will make it much harder to reuse components in previously unforeseen ways.

···

--
Kevin Lundberg

On Dec 7, 2015, at 2:12 PM, Javier Soto via swift-evolution <swift-evolution@swift.org> wrote:

This was brought up in a different thread about private by default. Creating a new thread for this. Quoting Mathew from the other thread with a short summary about the motivation behind this:

"It is not uncommon to have a need for a reference type without a need for inheritance. Superclasses should be intentionally designed to be subclasses and the author required to opt-in to subclassing and member overrides where that is required by the design."
--
Javier Soto
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


#11

Was subclassing always necessary for extending this functionality? Could
you accomplish the same using extensions?

Stephen

···

On Mon, Dec 7, 2015 at 2:21 PM, Kevin Lundberg via swift-evolution < swift-evolution@swift.org> wrote:

I can appreciate the desire for this, but I would not support it
personally. In a former life developing C# code where this is the default
behavior, I wanted to extend a class's behavior to make it work slightly
differently, but since everything is final by default (sealed in C#
parlance) I was prevented from doing so. The scope of my change was minimal
and well defined, and it would not have negatively impacted the extending
class's normal behavior. The code I was extending was open source so i
ended up having to copy and paste the class and its dependencies into my
project to do what I wanted. If I couldn't have done that then I'm not sure
what the right approach would have been.

In short, it is not always possible to know ahead of time how your clients
will want to extend your API's functionality; artificially limiting this in
my opinion will make it much harder to reuse components in previously
unforeseen ways.


(Kevin Lundberg) #12

Was subclassing always necessary for extending this functionality? Could you accomplish the same using extensions?

It's been a long time since that example so I don't remember every specific detail, but I recall needing to override one specific method that another class outside of my control called in order to intercept some behavior to tweak it slightly. I don't think extensions would have helped there unless I could have also swizzled my method with the original to keep the original's behavior as well (and I presume we don't want to introduce method swizzling as a first-class swift runtime feature).


(Lily Ballard) #13

I like this. I'm not convinced that this is something we should actually
do, but it seems like a better thing to make default than
final-by-default.

Regarding subclassing, I am sympathetic to the idea that it's a good
idea to mark classes as final. In my own experience, I've found that
just because you are capable of subclassing a class doesn't mean the
class will actually behave sanely when subclassed. I've adopted a
personal style of always defaulting my classes to final unless there's a
reason to allow subclassing, but this is also in the context of
application development (where you don't have to worry about
out-of-module subclasses anyway). In the context of framework
development, there's a lot more motivation to allow subclasses (because
you don't always know what behavior will need to be extended), and so
defaulting things to final-by-default will discourage that. Of course,
defaulting to sealed-by-default has a similar effect, and that's why I'm
not convinced that it's actually a good idea, but it does seem less
restrictive than final-by-default.

Another option would be to use sealed-by-default but to add a keyword
that means "subclassable" (such as `open`), and emit a warning whenever
a public class does not specify its subclassing nature; the warning
would be suppressed when explicitly marking the class as `sealed`,
`open`, or `final` (assuming of course that we add a keyword for
`sealed` to begin with).

-Kevin Ballard

···

On Mon, Dec 7, 2015, at 11:19 AM, Joe Groff via swift-evolution wrote:

There's a refinement of this idea we've discussed internally in the past.
Instead of making classes final by default, we could make it so that
public classes are not subclassable from other modules by default.


(Javier Soto) #14

Filling up the rest of the details for the proposal:

*Motivation*
"It is not uncommon to have a need for a reference type without a need for
inheritance. Superclasses should be intentionally designed to be
subclasses and the author required to opt-in to subclassing and member
overrides where that is required by the design."

*Proposed solution*
A keyword would be needed for both methods and classes. There are 2 options
(this is not a final proposal since I don't have great answers for all the
questions. Hoping other folks will chip-in :slight_smile: )
- The keyword to specify that a class is "inheritable" is the same as the
keyword to specify that a method is "overridable": this has the benefit of
adding fewer keywords to the language, but it conflates 2 ideas that are
not exactly the same.
- Different keywords

As for what those keywords could be: Is there precedent in other languages
for final-by-default? The only example I know of kind-of this is C++, where
methods have to be specified "virtual" (even though that doesn't have quite
the same semantics).
I dislike the term "virtual" because it conveys implementation details,
rather than developer intention. Ideally the keywords would communicate "I
meant for this class to be subclassable, and these are the methods that you
can / must override ", and users of Swift shouldn't have to know what a
v-table is.

I don't have great ideas for keywords. All the ones I can think of are:
"nonfinal" "inheritable" "overridable", which are not great to say the
least.

*Other aspects*
When methods are overriden, design often requires that the super
implementation be called, sometimes even at a specific time (before / after
the new implementation). Should this change also provide support for this
sort of pattern, or would that be beyond the scope? (Note that this was
possible in Obj-C with *NS_REQUIRES_SUPER*)

*Impact on existing code*
This change would break any classes that subclass a class that hasn't been
marked as "non-final", and all methods that override the implementation of
a method also not marked as "non-final".
The compiler could warn about this in the error however, with a message
along the lines of "*this class/method inherits/overrides a class/method
that's not marked with the "nonfinal" keyword*".

···

On Mon, Dec 7, 2015 at 11:12 AM Javier Soto <javier.api@gmail.com> wrote:

This was brought up in a different thread about private by default.
Creating a new thread for this. Quoting Mathew from the other thread with a
short summary about the motivation behind this:

"It is not uncommon to have a need for a reference type without a need for
inheritance. Superclasses should be intentionally designed to be
subclasses and the author required to opt-in to subclassing and member
overrides where that is required by the design."
--
Javier Soto

--
Javier Soto


(Joseph Lord) #15

I'm a strong supporter of the original proposal of default final classes (and would add to the arguments the performance gains of final even though the compiler can often finalise things anyway). I'm less sure about the within module special case sub classing behaviour (I'm not opposed but I'm not sure it is worth complicating the language for).

Most of the scenarios I can imagine could be implemented with an internal delegate property that provides for the specialisation of behaviour. Are there use cases that couldn't be managed in this way? I suppose it might be a cleaner way to modify varying amounts of the functionality by sub classing but I'm still not convinced for general development that it is worth expanding and complicating the language for different in module behaviour or addition sealed concepts.

Joseph
@jl_hfl

···

On Dec 7, 2015, at 7:19 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 7, 2015, at 11:12 AM, Javier Soto via swift-evolution <swift-evolution@swift.org> wrote:

This was brought up in a different thread about private by default. Creating a new thread for this. Quoting Mathew from the other thread with a short summary about the motivation behind this:

"It is not uncommon to have a need for a reference type without a need for inheritance. Superclasses should be intentionally designed to be subclasses and the author required to opt-in to subclassing and member overrides where that is required by the design."

There's a refinement of this idea we've discussed internally in the past. Instead of making classes final by default, we could make it so that public classes are not subclassable from other modules by default. Inheritance is manageable within a module, where all of the involved subclasses are revlocked and potentially have access to each other's implementations anyway, and the benefits of `final` and closed class hierarchy analysis are easy to recover with whole module analysis so don't necessarily need explicit calling out for internal interfaces. The problems with inheritance rear their head more with public interfaces, once code outside your control can subclass your classes.

-Joe


(Michel Fortin) #16

Let's say I write an app. It has many classes but not too much subclassing and not too much overriding. I compile it with full module optimization and it runs fast and everything is great.

Then I move most of that code to a library or framework to make sharing code easier. Everything works, but then I notice the performance has dropped significantly. Why? "All you have to do is to slap `final` in front of every class everywhere" someone on swift-users says. Ah! Why? Then a debate follows about dynamic dispatch, inlining, and whether it's a good idea to make everything final in a library.

Sealed by default makes a lot of sense to me because code that works great in an app still works great when moved to a library.

···

Le 7 déc. 2015 à 14:18, Slava Pestov via swift-evolution <swift-evolution@swift.org> a écrit :

Another idea we've discussed internally is some form of 'sealed' classes. That is, classes that are publicly visible but only subclassable from within their module. This could be used to define closed inheritance hierarchies which give stronger guarantees to the optimizer.

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


(Joe Groff) #17

I think a lot of people in this thread are conflating "final by default" or "sealed by default" with "sealed everywhere". No matter what the default is, the frameworks aren't going to suddenly rewrite themselves in Swift with final everything; Objective-C will still be what it is. Furthermore, we're only talking about language defaults; we're not taking away the ability for frameworks to make their classes publicly subclassable or dynamically overrideable. That's a policy decision for framework authors to make. The goal of "sealed by default" is to make sure the language doesn't make promises on the developer's behalf that they weren't ready to keep. ObjC's flexibility is valuable, and Apple definitely takes advantage of it internally all over place; Apple also has an army of compatibility engineers to make sure framework changes work well with existing software. Not every developer can afford that maintenance burden/flexibility tradeoff, though, so that flexibility is something you ought to opt in to. You can always safely add public subclassability and dynamic overrideability in new framework versions, but you can never take them back.

-Joe

···

On Dec 7, 2015, at 11:12 AM, Javier Soto via swift-evolution <swift-evolution@swift.org> wrote:

This was brought up in a different thread about private by default. Creating a new thread for this. Quoting Mathew from the other thread with a short summary about the motivation behind this:

"It is not uncommon to have a need for a reference type without a need for inheritance. Superclasses should be intentionally designed to be subclasses and the author required to opt-in to subclassing and member overrides where that is required by the design."
--
Javier Soto _______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Waite) #18

A typical class intermingle several implicit concepts in their design:
  - The public interface for external modules using the class
  - The internal workings and state of the implementation
  - The interface describing what behavior can and can’t be customized safely by subclasses, and how
  - Possibly an interface for privileged code (“internal” in Swift) to not expose publicly

public/private/internal, as well as final and the proposed sealed are rough tools to help in making these more explicit.

What I’m getting at is that in the absence of a completely well-designed class which takes all of these into account, the defaults affect what a typical class looks like.
- A choice of internal by default means that the class may accidentally not be exposed outside the module for public use, while a public default means that implementation details may be exposed accidentally
- A sealed class/final method by default means that a class may not be able to be customized in behavior, making the whole framework less useful. Non sealed by default means that classes can be customized, possibly without instruction in ways that are unsafe

Internal by default seems like a clear cut win - compiler errors or an example program will show classes which were meant to be exposed but were not. If public was default, leaked implementation details may go unnoticed.

Defaults of public sealed/final classes and final methods on a class by default are a tougher call. Either way you may have design issues go unnoticed until someone needs to subclass to get the behavior they want. So when you reach that point, should the system error on the side of rigid safety or dangerous flexibility?

The moral of the story is, make your classes sealed and your APIs use prototypes rather than the class directly :slight_smile:

-DW

···

On Dec 7, 2015, at 3:20 PM, Joseph Lord via swift-evolution <swift-evolution@swift.org> wrote:

On Dec 7, 2015, at 7:19 PM, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Dec 7, 2015, at 11:12 AM, Javier Soto via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

This was brought up in a different thread about private by default. Creating a new thread for this. Quoting Mathew from the other thread with a short summary about the motivation behind this:

"It is not uncommon to have a need for a reference type without a need for inheritance. Superclasses should be intentionally designed to be subclasses and the author required to opt-in to subclassing and member overrides where that is required by the design."

There's a refinement of this idea we've discussed internally in the past. Instead of making classes final by default, we could make it so that public classes are not subclassable from other modules by default. Inheritance is manageable within a module, where all of the involved subclasses are revlocked and potentially have access to each other's implementations anyway, and the benefits of `final` and closed class hierarchy analysis are easy to recover with whole module analysis so don't necessarily need explicit calling out for internal interfaces. The problems with inheritance rear their head more with public interfaces, once code outside your control can subclass your classes.

-Joe

I'm a strong supporter of the original proposal of default final classes (and would add to the arguments the performance gains of final even though the compiler can often finalise things anyway). I'm less sure about the within module special case sub classing behaviour (I'm not opposed but I'm not sure it is worth complicating the language for).

Most of the scenarios I can imagine could be implemented with an internal delegate property that provides for the specialisation of behaviour. Are there use cases that couldn't be managed in this way? I suppose it might be a cleaner way to modify varying amounts of the functionality by sub classing but I'm still not convinced for general development that it is worth expanding and complicating the language for different in module behaviour or addition sealed concepts.

Joseph
@jl_hfl

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(John McCall) #19

Our current intent is that public subclassing and overriding will be locked down by default, but internal subclassing and overriding will not be. I believe that this strikes the right balance, and moreover that it is consistent with the general language approach to code evolution, which is to promote “consequence-free” rapid development by:

  (1) avoiding artificial bookkeeping obstacles while you’re hacking up the initial implementation of a module, but

  (2) not letting that initial implementation make implicit source and binary compatibility promises to code outside of the module and

  (3) providing good language tools for incrementally building those initial prototype interfaces into stronger internal abstractions.

All the hard limitations in the defaults are tied to the module boundary because we assume that it’s straightforward to fix any problems within the module if/when you decided you made a mistake earlier.

So, okay, a class is subclassable by default, and it wasn’t really designed for that, and now there are subclasses in the module which are causing problems. As long as nobody's changed the default (which they could have done carelessly in either case, but are much less likely to do if it’s only necessary to make an external subclass), all of those subclasses will still be within the module, and you still have free rein to correct that initial design mistake.

John.

···

On Dec 7, 2015, at 7:18 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

Defaults of public sealed/final classes and final methods on a class by default are a tougher call. Either way you may have design issues go unnoticed until someone needs to subclass to get the behavior they want. So when you reach that point, should the system error on the side of rigid safety or dangerous flexibility?

This is a nice summary of the tradeoff. I strongly prefer safety myself and I believe the preference for safety fits well with the overall direction of Swift. If a library author discovers a design oversight and later decides they should have allowed for additional flexibility it is straightforward to allow for this without breaking existing client code.

Many of the examples cited in argument against final by default have to do with working around library or framework bugs. I understand the motivation to preserve this flexibility bur don't believe bug workarounds are a good way to make language design decisions. I also believe use of subclasses and overrides in ways the library author may not have intended to is a fragile technique that is likely to eventually cause as many problems as it solves. I have been programming a long time and have never run into a case where this technique was the only way or even the best way to accomplish the task at hand.

One additional motivation for making final the default that has not been discussed yet is the drive towards making Swift a protocol oriented language. IMO protocols should be the first tool considered when dynamic polymorphism is necessary. Inheritance should be reserved for cases where other approaches won't work (and we should seek to reduce the number of problems where that is the case). Making final the default for classes and methods would provide a subtle (or maybe not so subtle) hint in this direction.

I know the Swift team at Apple put a lot of thought into the defaults in Swift. I agree with most of them. Enabling subclassing and overriding by default is the one case where I think a significant mistake was made.


(Tino) #20

When methods are overriden, design often requires that the super implementation be called, sometimes even at a specific time (before / after the new implementation). Should this change also provide support for this sort of pattern, or would that be beyond the scope? (Note that this was possible in Obj-C with NS_REQUIRES_SUPER)

Imho something like this would be way better than „willSet/didSet“, which introduces a bunch of new keywords and is only usable for properties.
Declaring everything final by default sounds good in theory, but I think in reality, it will just make things complicated in most cases — and beside more options for the compiler to optimize, I don’t see any benefit in forbidding a subclass to be notified when one of its inherited methods gets called.
So, I would vote for NS_REQUIRES_SUPER to be the default (because it doesn’t hurt in most cases, and it’s so hard to find a proper keyword for that behavior :wink: