Scala 3 will no longer allow name clashes for nested classes. The following code will cease to compile and will have to be refactored:
class Base {
class Ops { ... }
}
class Sub extends Base {
class Ops { ... } // not allowed -- change name (or make private)
}
This compiles in the Scala 2.x series, but leads to confusion and compiler bugs. Usually, when members of classes (such as Ops) have the same name, we’d expect either overriding or overloading to occur. For class members, this is not the case, and the name is shadowed. Thus, forcing unique name choices will both be clearer to the user and much simpler to implement in the compiler.
One question for clarification: will a name clash be allowed for a private class? Technically, those are handled differently (they are not inherited, obviously, and thus do not play part in overriding).
In my use-case hiding all ancestors except for the parent class is much less confusing. I won’t be falling on the sword for this feature, but I’ll describe how I use it and why it’s more simple in my case.
I’m using name shadowing in my library to differentiate between a “user” API and a “developer” API, where a user just uses the API of the library and a developer can expand the library by knowing the internals. So I needed a way to clearly separate the two APIs, so the user isn’t exposed to a flood of irrelevant members, but the developer can still access them (both user and developer extend from the library’s traits and classes, so private[Lib] was not enough).
abstract class Foo {
trait __Dev {
def devFunc : Unit
}
lazy val __dev : __Dev = ???
def userFunc : Unit ={}
}
class Bar extends Foo {
trait __Dev extends super[Foo].__Dev {
def devFunc : Unit = println("only for devs")
}
override lazy val __dev : __Dev = new __Dev {}
}
So without shadowing and many extending classes, it the lowest class will now be “flooded” with __DevXXX member traits, instead of a single __Dev. Again, this is not crucial, but it does have its uses.
I agree that it can be inconvenient. But I believe the language simplification is worth it. Having same named members in subclass and superclass that don’t override each other, seems a small thing, but once you compromise on principles you set everything else adrift. Both Scala’s foundations and the Dotty compiler require that same named members override each other. So, essentially, we need to drop class shadowing or the whole Scala-3 project becomes unfeasible. Sorry.
So with such proposal we will not be able to decompose logic in inner classes.
If we want to override such composition we will have to use magical names.
I can understand it for classes(jvm reflection), but traits are something that have many “magics” in jvm so I really do not understand such principles for traits.
It makes scala closer to kotlin. But we have chosen scala out of its extensibility(orientation to high level business logic).
I believe scala should be extensible
If scala were “simple” it would be java\kotlin
That’s a good illustration of the point: They don’t override each other, it just looks this way.
Would You explain why trait shadowing makes Scala-3 project unfeasible?
The rules are universal: A type reference p.T consists of a path p and a type name T. It has a denotation, which might refer to a unique symbol, or not. Because p can have an intersection type or union type, it could have several members named T. In that case the denotation of the reference is the intersection (respectively union) of all member denotations. In all this discussion, there’s no place that a type refers to a unique “symbol” a priori (1). But that’s precisely what would be needed to support shadowing.
(1) Also note that in the DOT calculus there’s no notion of “symbol” anywhere, names is all you have got.
Ceylon’s model is to effectively render nested classes as members just like methods etc. and to make them overridable as such! This should alleviate @odersky’s concern:
as well, right? Or did I miss anything big that’d make this not feasible?
Or did I miss anything big that’d make this not feasible?
What you describe looks like virtual classes. It’s been an intensive research area for 15-20 years, stopped about 10 years ago. Why? Because the only formal treatment with a convincing soundness argument
is so restricted as to be unusable. Concretely you can refer to inner classes only via this. So this.C is OK, but x.C is not.
So, yes, people have tried it and several dozen papers were published. It did not work out.
Virtual classes, as they are also called, received a lot of attention in academia, including in the context of Scala, but I’m not aware of an approach that’s both practical and safe/sound. You can get quite close with abstract types and factory methods, but you’ll quickly run into problems that those types cannot safely be used because you don’t know which override (decided at runtime) you’re dealing with. Same problem as MyType.
As far as the examples I’ve seen here, how about one public base type and then a private implementation in each subclass. Private classes don’t cause name clashes.
Has research examined “extension classes” (in analogy to extension methods)? My hunch is that it would be both sound and useful, but probably not worth the complexity budget. (After all, you can express the same thing with a typeclass-like mechanism to bind the outer and “inner” types; your only problem there is visibility unless you have a friend mechanism like C++.)
Empty object is not a big deal, IMO. Inner classes and traits will be stored in extra class files anyway.
So what? You could still use them.
Inner trait of class has implicit reference to outer class which can be made explicit. Visibility of private methods is retained in my scheme. Look at this:
package no_shadowing
class Bfb_InventoryOrderDvi { self =>
import Bfb_InventoryOrderDvi._
def list(): List = new List {
override def outer: Bfb_InventoryOrderDvi = self
}
private def privateOfBfb_InventoryOrderDvi(): Unit = ()
}
class Bfb_InventoryOrderAvi extends Bfb_InventoryOrderDvi { self =>
import Bfb_InventoryOrderAvi._
override def list(): List = new List {
override def outer: Bfb_InventoryOrderAvi = self
}
private def privateOfBfb_InventoryOrderAvi(): Unit = ()
}
object Bfb_InventoryOrderDvi {
trait Default {
protected def outer: Bfb_InventoryOrderDvi
def callerOfBfb_InventoryOrderDvi(): Unit =
outer.privateOfBfb_InventoryOrderDvi()
}
trait List extends Default
trait Card extends Default
trait Lookup extends Default
}
object Bfb_InventoryOrderAvi {
import no_shadowing.{Bfb_InventoryOrderDvi => Super}
trait Default extends Super.Default {
override protected def outer: Bfb_InventoryOrderAvi
def callerOfBfb_InventoryOrderAvi(): Unit =
outer.privateOfBfb_InventoryOrderAvi()
}
trait List extends Default with Super.List
trait Card extends Default with Super.Card
trait Lookup extends Default with Super.Lookup
}
Show me code which works with your scheme, but doesn’t work with my scheme.
I do not undertand what do you try to prove me.
Lets disable shadowing at all(for inner variables etc)
And then I give you example wich will work.
Will it really help you?
I think it is good principle for system languages(‘c’). But it is awful for high level one it is a road to hell )))