1

I'm trying to template-out a TableQuery member but the path dependant types are messing me up. I've got a typical code-generated table like so:

trait TablesContainer {
    case class MembersRow(firstName:String, lastName:String)

    class Members(_tableTag: Tag) extends Table[MembersRow](_tableTag, Some("dbo"), "MEMBERS") {
        def * = (firstName, lastName) <> (MembersRow.tupled, MembersRow.unapply)

        val firstName: Rep[String] = column[String]("FIRM_NAME")
        val lastName: Rep[String] = column[String]("LAST_NAME")
    }
}

Then I try to create a class that will take a TableQuery (in the typical multi-db pattern) like so:

class TemplateHolder(profile: RelationalProfile) {
    import profile.api._

    class InsertionTemplate[T <: Table[E], E](tableQuery: TableQuery[T]) {
        def apply(insertRow:E) = {
          tableQuery += insertRow
        }
    }
}

Then I want to use it like so:

def template = new InsertionTemplate(TableQuery[TablesContainer.Members])

template.apply(MembersRow("Joe", "Bloggs"))

Only I get errors:

Error:(134, 28) inferred type arguments [com.test.TablesContainer.Members,Nothing] do not conform to class InsertionTemplate's type parameter bounds [T <: RelationalProfileConstants.this.profile.api.Table[E],E]
  def template = new InsertionTemplate(TableQuery[TablesContainer.Members])

and then this one:

Error:(134, 60) type mismatch;
 found   : slick.lifted.TableQuery[com.test.TablesContainer.Members]
 required: RelationalProfileConstants.this.profile.api.TableQuery[T]
    (which expands to)  slick.lifted.TableQuery[T]
  def template = new InsertionTemplate(TableQuery[TablesContainer.Members])

What am I doing wrong?

1 Answers1

4

The below code compiles without issues. The InsertionTemplate method was not able to infer type of E so explicitly providing the type parameter solved the issue. there could also be potential issues with your imported scopes. make sure you have the import statement import driver.api._ as shown below.

  trait Container1  {

    self: HasDatabaseConfigProvider[JdbcProfile] =>

    import driver.api._

    trait TablesContainer {
      case class MembersRow(firstName:String, lastName:String)
      class Members(_tableTag: Tag) extends Table[MembersRow](_tableTag, Some("dbo"), "MEMBERS") {
        def * = (firstName, lastName) <> (MembersRow.tupled, MembersRow.unapply)
        val firstName: Rep[String] = column[String]("FIRM_NAME")
        val lastName: Rep[String] = column[String]("LAST_NAME")
      }
    }
  }


  trait Container2 {

    self: HasDatabaseConfigProvider[JdbcProfile] with Container1 =>

    import driver.api._

    class InsertionTemplate[T <: Table[E], E](tableQuery: TableQuery[T]) {
      def apply(insertRow:E) = {
        tableQuery += insertRow
      }
    }

    class Run extends TablesContainer {
      // specify explicit type parameters.
      def template = new InsertionTemplate[Members,MembersRow](TableQuery[Members])
      template.apply(MembersRow("Joe", "Bloggs"))
    }
  }
rogue-one
  • 11,259
  • 7
  • 53
  • 75
  • Thanks rouge-one! Any idea how to do it if you can't put TablesContainer and InsertionTemplate in the same parent trait? I can't really do that because Slick generates it via the Codegen (technically I could extend the Codegen but that's a bigger headache...). This is especially problematic since Slick uses slick.driver.JdbcProfile inside the TablesContainer class and I want to use RelationalProfile so I can do in-memory testing. – Choppy The Lumberjack May 25 '17 at 20:01
  • @ChoppyTheLumberjack we can split the trait into two different traits if that helps you. I have updated the answer. Regarding using RelationalProfile.. you can use JDBCProfile and still do in-memory testing. I use H2 database to that. – rogue-one May 25 '17 at 20:29
  • Thanks again @rogue-one. So it seems that the only way to solve this problem is by caking on a layer on top of the thing that contains TablesContainer. Is that correct? – Choppy The Lumberjack May 26 '17 at 00:00