I have an use-case that would take advantage of case class attribute overriding if that was possible in Scala 2.12.x. My use-case is the following: there are multiple case classes in Play-Silhouette (P-S) e.g. LoginInfo
that the library is built upon:
case class LoginInfo(providerID: String, providerKey: String)
If life would be perfect, these would be traits and not case classes but ok, now in a project that re-uses P-S I'd like to design my Database, customize the Slick code generator and get Slick-mapped database-friendly definitions for those P-S case classes e.g.
case class LoginInfoRow(id: Int, override val providerID: String, override val providerKey: String, modified: Option[java.sql.Timestamp] = None)
extends com.mohiva.play.silhouette.api.LoginInfo(providerID, providerKey)
This approach would allow me to plug my Slick-persistence specialized LoginInfoRow
to the P-S framework seamlessly. Note that this is auto-generated from the database using Slick + my custom generator changes. The LoginInfoRow
definition above leads to the compiler error:
[error] /home/skywalker/code/play-silhouette-seed/app/models/generated/Tables.scala:29:14: case class LoginInfoRow has case ancestor com.mohiva.play.silhouette.api.LoginInfo, but case-to-case inheritance is prohibited. To overcome this limitation, use extractors to pattern match on non-leaf nodes.
[error] case class LoginInfoRow(id: Int, override val providerID: String, override val providerKey: String, modified: Option[java.sql.Timestamp] = None) extends com.mohiva.play.silhouette.api.LoginInfo(providerID, providerKey)
[error] ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
[error] Total time: 3 s, completed May 25, 2019 12:06:43 PM
A possible solution would be to have different names for those two attributes but it would be confusing from the interface of the LoginInfoRow
which one to use? and it would also duplicate storage.
What's this compiler proposed alternative To overcome this limitation, use extractors to pattern match on non-leaf nodes.
?
You could write an implicit or explicit converter for this scenario.
Implicit converter:
implicit def loginInfoRowToLoginInfo(loginInfoRow: LoginInfoRow): LoginInfo = {
LoginInfo(loginInfoRow.providerID, loginInfoRow.providerKey)
}
Explicit converter:
case class LoginInfoRow(
id: Int,
providerID: String,
providerKey: String,
modified: Option[java.sql.Timestamp] = None
) {
def toSilhouette: LoginInfo = {
LoginInfo(providerID, providerKey)
}
}
Or a combination of both.