Home > database >  How to use an Object in a trait by using its type in Scala
How to use an Object in a trait by using its type in Scala

Time:08-29

I was trying to implement some Model - Row implementation similar that Rails (Activerecord) in Scala. I simply trying to do this;

trait Model[A]:
  def query = "model has this good stuff I want to use in RowData"

trait RowData[B <: Model[B]]:
  def query = B.query

case class Person(name: String, age: Int) extends RowData[Person]

object Person extends Model[Person]

Person("onur", 38).query // I wan't to use like this

I'm getting Not found: B on RowData trait. Because Model[A] is an object I supposed that I can use it as is.

You can try in here: https://scastie.scala-lang.org/U3MOJhFXSS2O8fanY5aIpg

CodePudding user response:

trait RowData[B <: Model[B]]:
  def query = B.query

This is not supported because (among other reasons) the query method might not exist.

The problem is that the type of the companion object is not related to the type of the class. So the fact that B is a subclass of Model[B] says nothing about the type of the companion object of B. And specifically, it does not require the companion object of B to be a subclass of Model[B] and therefore it is not required to have the appropriate query method.

The other problem is that this does not compile:

trait RowData[B <: Model[B]]:
  def query = B.query

case class Person(name: String, age: Int) extends RowData[Person]

The RowData trait requires that Person is a subclass of Model[Person] but Person does not inherit from Model[Person]

You either need to have Person inherit from Model and provide the appropriate query method, or look at using a type class for this problem.

CodePudding user response:

It seems the requirement is to use companion object's method, but the issue with this solution is that,

trait RowData[B <: Model[B]]:
  def query = B.query

Because B is supposed to be an Object, it cannot be passed as a type in []. You need to pass it as an Object in a parenthesis (), like this

trait Model[A]:
  def query = "model has this good stuff I want to use in RowData"

trait RowData(B: Model[_]):
  def query = B.query

case class Person(name: String, age: Int) extends RowData(Person)

object Person extends Model[Person]

Person("onur", 38).query

However, this is not an ideal solution either, because B is not enforced to be the companion object you pass for the row data class. In Scala, we commonly use Context Bound to enforce this, like this:

trait Model[A]:
  def query = "model has this good stuff I want to use in RowData"

trait RowData[B : Model]:
  def query = summon[Model[B]].query

case class Person(name: String, age: Int) extends RowData[Person]

given Person: Model[Person] with {} //Implement custom query method here

Person("onur", 38).query
  • Related