1
votes

I am quite new to Scala programming. Besides Scala, I have never learned functional programming. With that said, I am currently trying to figure out a good way to define traits, classes, etc, on a project using slick.

My idea was to have a trait having the methods to be implemented on the child classes, and some would be implemented on the parent class itself. I found a way that works but I have no idea why. Not sure if this problem is related to how Slick or Scala works.

I was using a construction like:

trait CompanyDAO extends BaseDao[Company]{
 self: DBProfile  =>

But this give the following type mismatch error:

[error] found : slick.lifted.TableQuery[CompanyDAO.this.CompanyTable] [error] required: slick.lifted.TableQuery[CompanyDAO.this.profile.api.Table[Company]] [error] (which expands to) slick.lifted.TableQuery[CompanyDAO.this.profile.Table[Company]] [error] Note: CompanyDAO.this.CompanyTable <: CompanyDAO.this.profile.api.Table[Company], but class TableQuery is invariant in type E. [error] You may wish to define E as +E instead. (SLS 4.5) [error] override def toTable = TableQuery[CompanyTable]

But, if I use

self: DBProfile with BaseDao[Company] =>

Then the compilation works (BTW, got the solution from another post)

So, my questions:

1) Why using self-types toTable assignment works while extending the trait do not? How scala interprets the type of toTable in both scenarios?

2) Is there a way to adapt the "trait CompanyDAO extends BaseDao" to solve the error?

Thank you in advance.

import scala.concurrent.Future
import slick.basic.DatabaseConfig
import slick.jdbc.JdbcProfile

trait DBConfiguration {
  lazy val config = DatabaseConfig.forConfig[JdbcProfile]("mytrade")
}

trait DBProfile {
  val config: DatabaseConfig[JdbcProfile]
  val db: JdbcProfile#Backend#Database = config.db
  val profile : JdbcProfile = config.profile
}

trait BaseDao[T <: Any] {
  self: DBProfile =>

  import profile.api._
  import slick.lifted.TableQuery

  def toTable():TableQuery[Table[T]]
  def findAll():Future[Seq[T]] = db.run(toTable.result)
}

case class Company(name: String, code: Int)

// If I use the construction like the comment below, it will fail
//trait CompanyDAO extends BaseDao[Company]{
    //self: DBProfile  =>

trait CompanyDAO {
  self: DBProfile with BaseDao[Company] =>
  //import from DBProfile trait
  import profile.api._

  class CompanyTable(tag: Tag) extends Table[Company](tag, "COMPANY") {

    import slick.ast.BaseTypedType
    import slick.jdbc.JdbcType

    def name = column[String]("name")
    def code = column[Int]("code")

    def * = (name, code) <> (Company.tupled, Company.unapply)
  }

  override def toTable = TableQuery[CompanyTable]
}

EDIT: some other things I have been trying

Extending BaseDao, If I change the declaration of toTable to:

def toTable[S <: TableQuery[Table[_]]]():S

The type mismatch goes away but I receive now:

test.scala:27: dead code following this construct [error] def findAll():Future[Seq[T]] = db.run(toTable.result)

Tried also using self-type and it gave me the same error.

1

1 Answers

0
votes

If I change the declaration of toTable to:

def toTable[S <: TableQuery[Table[_]]]():S

The type mismatch goes away but I receive now:

test.scala:27: dead code following this construct [error] def findAll():Future[Seq[T]] = db.run(toTable.result)

Any ideas why is this happening?

I can't reproduce your specific compile error.

But when I change the line

def toTable():TableQuery[Table[T]]

to

def toTable[S <: TableQuery[Table[_]]]():S

I have compile error

Error:(24, 51) value result is not a member of Nothing
    def findAll():Future[Seq[T]] = db.run(toTable.result)

This is because type parameter S is inferred to be Nothing.

You don't provide implementation for the method toTable and Nothing <: TableQuery[Table[_]].