11

I am working on an abstract CRUD-DAO for my play2/slick2 project. To have convenient type-safe primary IDs I am using Unicorn as additional abstraction and convenience on top of slicks MappedTo & ColumnBaseType.

Unicorn provides a basic CRUD-DAO class BaseIdRepository which I want to further extend for project specific needs. The signature of the class is

class BaseIdRepository[I <: BaseId, A <: WithId[I], T <: IdTable[I, A]]
  (tableName: String, val query: TableQuery[T])
  (implicit val mapping: BaseColumnType[I])
  extends BaseIdQueries[I, A, T]

This leads to DAO implementations looking something like

class UserDao extends 
  BaseIdRepository[UserId, User, Users]("USERS", TableQuery[Users])

This seems awfully redundant to me. I was able to supply tableName and query from T, giving me the following signature on my own Abstract DAO

abstract class AbstractIdDao[I <: BaseId, A <: WithId[I], T <: IdTable[I, A]] 
  extends BaseIdRepository[I,A,T](TableQuery[T].baseTableRow.tableName, TableQuery[T])

Is it possible in Scala to somehow infer the types I and A to make a signature like the following possible? (Users is a class extending IdTable)

class UserDao extends AbstractIdDao[Users]

Is this possible without runtime-reflection? If only by runtime-reflection: How do i use the Manifest in a class definition and how big is the performance impact in a reactive Application?

Also, since I am fairly new to the language and work on my own: Is this good practice in scala at all?

Thank you for help. Feel free to criticize my question and english. Improvements will of course be submitted to Unicorn git-repo

EDIT: Actually, TableQuery[T].baseTableRow.tableName, TableQuery[T] does not work due to the error class type required but T found, IDEA was superficially fine with it, scalac wasn't.

Floscher
  • 438
  • 3
  • 10
  • See this other answer for an explanation of the error on `TableQuery[T].baseTableRow.tableName`. The error is emitted inside a macro, this is probably the reason why IntelliJ is unable to see detect it. – Régis Jean-Gilles Apr 25 '14 at 16:12

1 Answers1

0

As for your first question, I've encountered this when working with Slick too. But if you think about it, you'll see you cannot do this at compile time. This is because this type information is necessary specify the relations between your type parameters. If you would not, you would be able to construct classes of BaseIdRepository where the types don't make sense, such as IdTables where the table doesn't represent the projection. Since you need names for each of these relations, you need 3 named type parameters. If you omit the first one, it is possible to construct an IdRepository without a projection containing an Id; if you omit the second one it is possible to have a table without an ID column; and if you omit the third one, it is possible to query tables that do not have this combination of a table and a projection with an ID. You might not have the types defined in your application that would break any of these rules presently, but the compiler doesn't know that. Supplying the proper type information is unavoidable.

As for your second question, it is very unadvisable to employ reflection just because you think the syntax is verbose. If you can make guarantees about typesafety by simply by providing type parameters, I would advise you to do so. It is in very bad taste and style to write Scala in such a way. It would be ironic to employ typesafe ID's with Unicorn and later hack around its type safety with reflection.

Furthermore, a Manifest is not what you want: a manifest doesn't allow you to provide less type information to the compiler, it only allows you to be more flexible to specify where you do so. It allows you to leverage the compiler's knowledge of types at compile time to circumvent some issues that type erasure introduces. The problem you face here has nothing to do with type erasure, so Manifests won't work. Lastly, runtime reflection won't help you much here because Slick's internal functions won't allow you to compile if you don't already supply the type information.

So yeah, what you want is impossible. Scala (and Slick) need complete information at compile time and no trick is going to be effective in circumventing that.

DCKing
  • 4,253
  • 2
  • 28
  • 43