I would wish all concrete classes would be final by default in Scala 3.
Making a class “open” should require the new modifier keyword overridable (a nice symmetry with override) on the class or one of its members.
I think such a change could be handled by ScalaFix and would not need any manual interaction to preserve the programm’s semantics.
Concerning “repair / modification from the distance” of library classes / objects (traits?): It would be nice if Scala would get some AOP (aspect-oriented programming) support for that.
Only an opinion from some random guy on the internet…
Is there more of an argument for making final the default than for making private the default?
If the writer of a library should have to be explicit about what parts of the library can be used in what way, doesn’t that extend to method access? Shouldn’t public APIs have to be marked as such explicitly?
Actually there is. See the interview posted by @alexandru. It is much more difficult to maintain and reason about the outgoing contract (which is only a concern for non-final) than the incoming contract (which applies to everything non-private).
To answer all the question at once (out of my perspective): No, no, and no.
The messages you can send to an object are its public interface, aren’t they?
Methods correspond to messages so it’s quite natural that they are public by default. A non public method on the other hand is something special. It does not correspondent to a message. It’s an implementation detail! So it should be marked explicitly as such.
If classes wouldn’t be overridable by default there is even no strong need for hiding the methods anyway.
I strongly agree with those words. I feel it is far more practical to loosely declare which APIs are intended to be used and which are internals that should be used with care.
To continue this line of thought, I have to say that I also very much dislike when library authors restrict the scope of many of their classes to only be used by their library (private[mylibrary]). It has been too often that I just had to give up a desired feature or a bug fix in a big library (akka, scalikejdbc, etc) just because of restricted scopes. I’d rather have the authors mark those parts as internals (perhaps via annotation which would yield a compiler warning) than restrict them entirely.
I agree with a previous comment that it would be nice to have tooling support to crack open the implementation details of a class in order to apply a patch or test non-public implementation details.
These implementation details might be restricted members, or they might be local functions.
There are endless debates about whether it is wise to test non-public implementation details, where you would sprinkle asserts for invariants, and I don’t mean to reopen that side of the debate. But I would like to add here that local functions are not different from private methods with regard to testing and patching hacks.
It seems like there are two views on this – perhaps it’s largely people who value freedom to modify other people’s libraries vs. library authors who value freedom to modify their own libraries without breaking other people’s code…
(Personally I’m more in the former camp.)
In any case, it seems like a viable solution would have to satisfy both camps, and of course not break existing code.
As you probably know, in Java, the default visibility is package privage which I personnaly think is the correct default if you think of your system as being assembled from components. public and private require explicit annotations which supposedly make them that slight bit harder to use therefore encouraging good design. Sadly the thoughtfulness put in choosing this default was totally defeated by IDEs making everything public by default in their templates and upon usage.
the interesting point about package private is that it is not impossible for someone with sufficient motivation to overcome the encapsulation from outside the library code base. One can create a class in the library’s package to access whatever one needs.
Could the same thing be applied to final ?
final : no one can touch this
package final: no one can extend/override this from outside the package (would be the default)
override: you are encouraged to extend/override this.
(override may not be the best way to express this but it has the strong advantage of not requiring the creation of a new keyword. adding keywords to a language is always quite painful for end users).
I don’t know the JVM/compiler well enough to know if this is achievable or not
This proposal (@nafg’s) is actually very sensible. I would endorse it.
I would just keep @open in dotty. There are still use cases for open concrete classes even when traits can get parameters. The most important ones are Java interop and their properties wrt binary compatibility.
Excuse my ignorance but what would be gained by this proposal? I don’t get it.
I read this as: Everything stays the same except there would be a new annotation that suppresses some warnings.
Did I misunderstood something? What problem is solved this way? Could you explain please?
The original proposal solves a real problem, and would make a best practice the default (also helping newcomers to the language doing “the right thing” without thinking about it). The coast of that change is also low. It would be a fully automatic rewrite. In some well written code-base it would also reduce the visual line noise by removing the “final” keyword that is used in almost every declaration. Such change would also make “open” (or “overridable” which I prefer due to symmetry but that’s a bikeshed color anyway) visually stand out, marking clearly the places some external implementation can hook in.
Removing classes from the language does seam problematic also. How would an code upgrade to scala 3 look like? Can really every class be simply rewritten to a trait? I’m not sure about that.
And like I said: Extending random alien classes not owned by yourself isn’t the clean approach to “bugfixing” / “experimenting with” library code in my opinion. AOP would be a much cleaner pattern to achieve the same goal.
No matter the exact keyword used for this, if this feature does get implemented with an open keyword or similar, it doesn’t have to be backwards incompatible wrt the keywordization, right? Couldn’t open (or overridable) be somewhat like Java 9’s new restricted keywords, i.e. normal identifier unless it appears in a context where an identifier couldn’t have been before in the first place (as we have @annotations unlike Ceylon).
Or is the implementation of that/technical debt too high for consideration? (As I have not much insight into the compiler’s design)
No, it adds warnings. The annoyance is that the “best practice” would require writing the annotation by default. I’d actually prefer having the annotation for (b).
We’ve chatted about it on the Dotty side, it looks plausible but we haven’t tried out how invasive it’d be, and it’s not been a priority yet.
AOP isn’t coming to Scala. AOP research has been mostly abandoned, because AOP is too powerful and fragile, especially if the advised code evolves without accounting for the aspects. Even its flagship conference (http://modularity.info/) died out / was renamed into something else.
We understand the tradeoffs of the OOP approach much better, even tho it’s not perfect, and we have some sensible guidelines (see Anders Hejlsberg’s interview); AOP has all the same problems (and more), but magnified.
I admit “final by default” would be cleaner, but even with ScalaFix the transition cost is too big, as ScalaFix doesn’t rewrite existing knowledge in either heads or docs.
I think that’s only sort-of supported, code using that will sometimes break, especially in libraries and breaks at least with OSGi; I don’t use it myself, but some libraries need to.
The author does not support users that extend it, but does not forbid it.
The author explicitly forbids extension.
#2 is probably the best default. In Java some libraries use an annotation to make it clear when it is case #2 and leave the language features for #1 and #3. Users that extend classes in case #2 are on their own if they are broken by later changes.
I do not think it is a good decision. We often make library fasade and final class is often headache.
We can make public trait and private class.
And nobody will have problem.
Trait cannot be overridden by mistake
It is very easy to decorate trait
So library users will be happy, I will definitely be
Non-sealed traits cannot receive new fields in backward binary compatible ways, even private ones. They are usually not suitable for evolvable library design.
we use java interfaces to separate the declaration from implemantation a lot in java. In scala we use traits for that perpose. It is sad that there are no simple way to do it in scala (((
Plain interfaces is a very good thing, may be the real problem is its absence?