Introducing Trait Parameters in Scala 3

Reading Time: 2 minutes

At Knoldus, we are endeavoring to learn Scala 3 and share it with community. There are multiple blogs which have been published here on Scala 3 which you can visit to get to know about other new features of the language. In this blog post, I will take you through the feature of trait parameters introduced in the language.

Traits in Scala 2

Traits are a powerful feature of Scala which help in structuring ADTs, reusing components and in preventing pattern matching errors (sealed traits). But in Scala 2, one thing that we cannot do with traits is to pass parameters to them. So, we would need to something as shown in the below example.

object Order extends App {
trait Order {
val quantity: Int
val printQuantity: String = s"This order has a quantity of $quantity"
}
class Buy(q: Int) extends Order {
override val quantity: Int = q
}
class Sell(q: Int) extends Order {
override val quantity: Int = q
}
object Trade {
def buy(q: Int): Buy = new Buy(q)
def sell(q: Int): Sell = new Sell(q)
}
val buy1 = Trade.buy(100)
println(buy1.quantity)
println(buy1.printQuantity)
}
view raw Order.scala hosted with ❤ by GitHub

As the output clearly shows, though the quantity of buy1 is 100, when invoking printQuantity it prints 0. This is called early initialization.

A potential workaround is early definitions which would look like as follows:

import Order.Order
object OrderWithWorkaround extends App {
class BuyWithWorkaround(q: Int) extends {
val quantity = q
} with Order
class SellWithWorkaround(q: Int) extends {
val quantity = q
} with Order
object TradeWithWorkaround {
def buy(q: Int): BuyWithWorkaround = new BuyWithWorkaround(q)
def sell(q: Int): SellWithWorkaround = new SellWithWorkaround(q)
}
val buy2 = TradeWithWorkaround.buy(100)
println(buy2.quantity)
println(buy2.printQuantity)
}

The solution is not very intuitive to many and becomes complicated quite often.

Scala 3 Trait Parameters

With Scala 3 introducing the feature of trait parameters, the solution to the above scenario becomes cleaner and easy to understand. Look at the following code from Scala 3 –

object TraitParameters extends App {
trait Order(quantity: Int) {
val printQuantity: String = s"This order has a quantity of $quantity"
}
class Buy(q: Int) extends Order(q)
class Sell(q: Int) extends Order(q)
object Trade {
def buy(q: Int): Buy = new Buy(q)
def sell(q: Int): Sell = new Sell(q)
}
val buy1 = Trade.buy(100)
println(buy1.printQuantity)
}

Of course, as traits can take parameters now and as we know that multiple traits can be mixed in hierarchy, this feature could have resulted in ambiguities. To prevent this, Scala has laid the following rules and anything other than that is treated as illegal – 

  1. If a class C extends a parameterized trait T, and its superclass does not, C must pass arguments to T.
  2. If a class C extends a parameterized trait T, and its superclass does as well, C must not pass arguments to T.
  3. Traits must never pass arguments to parent traits.

The following is illegal in Scala 3.



This brings us to the end of this blog. I hope you got to learn something. Feel free to drop us a note in case you have questions. Do checkout more blogs written by our engineers.

References
https://dotty.epfl.ch/docs/reference/other-new-features/trait-parameters.html
https://docs.scala-lang.org/sips/trait-parameters.html

Knoldus-blog-footer-image

1 thought on “Introducing Trait Parameters in Scala 32 min read

Comments are closed.