Proposal to add Trait Parameters to the Language


#1

Here’s a proposal to add trait parameters to Scala 3. The proposal allows traits to have parameters, just like classes have parameters. Example:

trait Greeting(val name: String) {
  def msg = s"How are you, $name"
}
class C extends Greeting("Bob") {
  println(msg)
}

Here’s a link to the doc page.

Motivation

The main reason for adding trait parameters is more regularity. If classes can take parameters, the same should hold for traits. There is a question about who gets to pass arguments to a trait, since traits can be multiply extended by several other traits or classes. The proposal has a simple answer to that question: The first class extending a parameterized trait must pass corresponding arguments. I believe this makes the proposal simple enough to be a win.

Another reason for adding trait parameters is that they can replace most of the use cases of early initializers, which we would like to eliminate. Early initializers are a complex and somewhat “exotic” language feature which many did not even know about. By contrast, trait parameters are simple, intuitive and they increase the regularity of the language.

This proposal is open for discussion in the community and will be discussed in our next SIP meeting, where we’ll take all your feedback into account for the approval/dismissal of this feature.


#2

How does this interact with implicit parameter blocks?

trait Something1
trait Something2
trait A(implicit st1 : Something1)
trait B(implicit st2 : Something2)
trait AB extends A with B

implicit val myST1 = new Something1{}
implicit val myST2 = new Something2{}
class ABC extends AB //Should this work?

Will the code above work?


#3

You have to change the last line to

class ABC extends A with B with AB 

since parameters must be passed directly from class to trait. But then it works.


#4

I understand the requirement in the general case. I’m wondering if implicit parameters like in the example above can have an exception of this rule.


#5

Additionally, there is an inconsistency with implicit parameters in abstract classes. Consider the following Scala 2 code:

trait Something
abstract class A(implicit st : Something)
trait MyA extends A

object Test {
  implicit val myST = new Something {}
  new MyA {}
}

But with trait parameters this will look like:

trait Something
trait A(implicit st : Something)
trait MyA extends A

object Test {
  implicit val myST = new Something {}
  new A with MyA {}
}

#6

I understand the requirement in the general case. I’m wondering if implicit parameters like in the example above can have an exception of this rule.

Implicit parameters expand to calls with compiler filled-in parameters. So the rules for explicit parameters have to apply to implicit ones as well.


#9

Can’t the compiler complete the missing “inheritance pieces” ?

trait Something1
trait Something2
trait A(implicit st1 : Something1)
trait B(implicit st2 : Something2)
trait AB extends A with B

implicit val myST1 = new Something1{}
implicit val myST2 = new Something2{}
class ABC extends AB //Compiler will automagically change the last line to 
class ABC extends A with B with AB //Compile rewrite

Alternatively, it may be possible to do via plugin, scalafix, or IDE generation, but IF it is possible, then why shouldn’t the compiler just do it by itself?
Is there something I don’t see where? What kind of language rules could this break?


#10

One could do that, but it feels like a little bit too much magic for me. I’d leave it off the proposal, but it could be considered in a later SIP.


#11

Might be a simple question but let’s clarify: whats left to differentiate abstract class from trait?


Second batch of Scala 3 SIPs: additions to Scala's type system
#12

Internally to Scala:

  • A trait B that extends another another trait A cannot pass parameters to A.
  • A class C that extends A can pass parameters to A.

Regarding interop with Java/JavaScript:

  • A Scala class is a class from Java’s or JavaScript’s point of view
  • A Scala trait is an interface from Java’s point of view, and does not exist from JavaScript’s point of view

#13

I would say that a class is where you fix the linearisation order. As long as you stay with traits, you don’t (statically) know where a super call will go. When you mix them all together into a class, you’ve now locked that order down.


#14