Proposal to disallow class shadowing

In our use case we do override each other

 trait Default extends super.Default

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

Sorry, I have been a bit emotional in previous message.

Would You explain why trait shadowing makes Scala-3 project unfeasible?

1 Like

In our use case we do override each other

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.

@AMatveev:
Wouldn’t moving inner traits to classes’ companion objects solve shadowing problems?

Instead of:

class Bfb_InventoryOrderAvi extends Bfb_InventoryOrderDvi {

  override def list(): List = {
    new List {
      override def meta = this
    }
  }
  trait Default extends super.Default{
     @Setter
    override def setidObj(event: SetterEvent): Unit = {
      super.setidObj(event)
      selection.refreshItem()
    }
  }
  trait List extends Default with super.List
  trait Card extends Default with super.Card
  trait Lookup extends Default with super.Lookup
}

Write:

package no_shadowing

class Bfb_InventoryOrderDvi {
  import Bfb_InventoryOrderDvi._
  def list(): List = new List {}
}

class Bfb_InventoryOrderAvi extends Bfb_InventoryOrderDvi {
  import Bfb_InventoryOrderAvi._
  override def list(): List = new List {}
}

object Bfb_InventoryOrderDvi {
  trait Default

  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

  trait List   extends Default with Super.List
  trait Card   extends Default with Super.Card
  trait Lookup extends Default with Super.Lookup
}

I do not think it is the best decision for us.

  • it will create unnecessary inner object
  • And more importantly, we occasionally use type parameters for example find[Bfb_InventoryOrderDvi.Bfb_InventoryOrderDvi.List]()

So of course it will work, but it will not look very pleasant.
I think we will use magic prefixes, and we will write yet another style guide :))

note:

trait List   extends Default

We need that ‘List’ would be inner class of dvi

Thank you, I have understood motivation.

Would it be possible to implement ability which allows to disable inheritance but use such traits from others classes? for example:

class Bfb_InventoryOrderDvi extends View {

  isolated trait Default extends View.Default{
    
  }
  isolated  trait List extends Default with View.List
}

class Bfb_InventoryOrderAvi extends Bfb_InventoryOrderDvi {
  import no_shadowing.{Bfb_InventoryOrderDvi => Super}
  isolated trait Default extends Super.Default{
  }
  isolated  trait List extends Default with Super.List
}

Otherwise with such proposal we will have to add class name prefix to inner trait or use object. In any case it will not be very comfortable.

If someone feels moved to write an introductory book to Dot and the Scala 3 compiler, I would certainly buy it.

I am not sure how well it’d integrate into DOT and also into @AMatveev’s and @soronpo’s use cases, but what about switching to a model quite like Ceylon’s (formal/default) member classes?

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.

1 Like

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.

1 Like

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++.)

if I understand your proposal correctly it will not work in multi layer decomposition.

For example, we have several layers in my example which is written above:

  • core layer (interfaces from application server)
  • application layer (default implementation)
  • code generation layer (dvi)
  • business logic layer (avi)
  • customisation layer (optional for specific configuration)

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 )))

AFAIU your main concern was that disabling class shadowing will force you to use extra prefixes/ suffixes:

What I shown is that you do not need that even with class shadowing disabled.

I’m just curious whether your argument (i.e. disabling class shadowing forces to use “magic names”) is valid. IMO you just need some refactoring (that I shown) and class/ trait/ etc names will stay the same.

Sorry, but you have not shown it.
Your proposal not to use inner traits is very doubtful. Are you really trying to prove that inner traits are useless?
Your proposal to use objects is as usful for us as prefixes

What are the deficiencies?

Not at all. OTOH, I think that if you change code generator a bit then you don’t need to come up with “magical names” nor do you need substantial changes in client code.

Well, use whatever you want. I still don’t think there’s any serious problem with disallowing class shadowing. However, I would be glad if you prove me otherwise, i.e. pinpoint a problem with my scheme that I’m not aware of.

So we have agreed that we use inner traits. So your example is not completely correct.

My example is a client code, for businesses logic and customization layers.

Ok, I have said already if there were not shadowing at all it would not be a real problem. But it would be a real headache somtimes at least for me.

I think the lack of shadowing leads us to “global variables”. It is not a real problems of course , but it is a bad thing.

By “client code” I meant code that uses the generated classes.

I think you’re confusing shadowing with overriding. Class members in Scala can and will be able to be overridden.

What shadowing gives you except for reusing the same name for a potentially completely different thing? Remember that shadowing doesn’t care about types, so for a compiler it doesn’t matter if you shadow a parent class with a child class. You don’t gain extra flexibility from that, AFAIU.

1 Like