noru
noru

Reputation: 526

Scala Type Mismatch issue when using import from different scope

In my project(play 2.4 with slick 3.0), I have a setup: using MySQL for production and H2 for unit testing(start to feel that it is a horrible idea). To do that, I have to import slick.driver.JdbcProfile instead of any specific driver (e.g. slick.driver.MySQLDriver) and import its api._ in my DAO class, like this:

class SomeDAO(val context: MyContext){
  import context.profile.api._
  import context.SOME_TYPE

  def invoke(para: SOME_TYPE) = { ... }

  // other DBIO...

}

Everything is OK so far, however, I have a service class that requires this DAO, and a type in context as a parameter:

class MyService(val context: MyContext){

   val someDAO = new SomeDAO(context)
   import someDAO.context.SOME_TYPE  // it works
   // import context.SOME_TYPE   // Type mismatch! But how to avoid it???

   def invokeDAOMethod(para: SOME_TYPE) = someDAO.invoke(para) // where mismatch occurred
   // expect: MyService.this.someDao.context.SOME_TYPE
   // actual: MyService.this.context.SOME_TYPE

}

Problem heppens when I try to import a type from exact the same context instance, intuitively speaking, I'm using the "Same" type here, right?

Can someone explain this kind of behaviour and give hint about patterns to cut this kind of 'non-sense' I wrote?

Upvotes: 2

Views: 1484

Answers (2)

ayvango
ayvango

Reputation: 5977

You are using path-dependent types. They are literally dependent on paths, not on its content.

Consider that example:

class Store {
  case class Box[T](box : T)
  def box[T](b : T) = Box[T](b)
  def unbox[T](b : Box[T]) : T = b.box
}

object Assign {
  val x = new Store()
  val y = x
  val box = x.box[Int](2)
  val ub = y.unbox[Int](box)
}

You may assume naively that x and y are practically identical, they share the same content and the same type. True for the content. Wrong for types. And compiler nicely provide you with a type error:

error: type mismatch;

found : dependent.Assign.x.Box[Int]

required: dependent.Assign.y.Box[Int]

But you could tell the compiler that x and y should share the same path-dependent types despite being different literal paths

object Assign {
  val x = new Store()
  val y : x.type = x
  val box = x.box[Int](2)
  val ub = y.unbox[Int](box)
}

With val y : x.type = x the compiler would success.

Your issue has the similar nature, and the solution is similar too: you should specify type equivalence explicitly. Change the SameDAO definition to accept type parameter

class SomeDAO[MC <: MyContext](val context: MC){
  ...
}

and pass appropriate type upon creation:

class MyService(val context: MyContext){
  val someDAO = new SomeDAO[context.type](context)
  ...
}

Upvotes: 4

noru
noru

Reputation: 526

Reduce the pain by moving the type definition to a object, therefore the type is imported statically.

Still not sure how to inherit types and how to introduce some dynamic components into this object

UPDATE: it is actually called "Path Dependent Types" in Scala.

Upvotes: 0

Related Questions