Problem: Describe a record with an ID field (which makes it an Entity) The ID field will need to be autogenerated, so that Record (also known as A) + ID = Entity (also known as B)
trait Record extends Product
trait Entity {
type Id
}
case class Book(title: String, author: String, publication: Int)
case class PersistentBook(id: Long, title: String, author: String, publication: Int) extends Entity {
type Id = Long
}
object PersistentRecords {
def main(args: Array[String]): Unit = {
val bookGen = Generic[Book]
val persistentBookGen = Generic[PersistentBook]
val scalaBook = Book("Programming in Scala", "Odersky, Spoon, Venners", 2008)
val scalaBookHlist = bookGen.to(scalaBook)
val persistentScalaBookHList = 15L :: scalaBookHlist
val persistentScalaBookFromGeneric: PersistentBook = persistentBookGen.from(persistentScalaBookHList)
println(s"Book: $scalaBook")
println(s"PBook: $persistentScalaBookFromGeneric")
val genHListScalaBook = injectFieldSimpleGeneric(scalaBook, 15L)
println(s"GenBook: $genHListScalaBook")
val persistedScalaBook = injectFieldGeneric(scalaBook, 16L)
println(s"PersistedBook: $persistedScalaBook")
}
// OK
def injectField[F](baseRecord: HList, field: F): HList =
field :: baseRecord
// OK
def injectFieldSimpleGeneric[A, ARepr <: HList, F](baseRecord: A, field: F)(implicit aGen: LabelledGeneric.Aux[A, ARepr]): HList = {
val baseHList = aGen.to(baseRecord)
val compositeHList: HList = field :: baseHList
compositeHList
}
def injectFieldGeneric[A, ARepr <: HList, B <: Entity, BRepr <: HList, F <: Entity#Id ](baseRecord: A, idField: F)(
implicit aGen: LabelledGeneric.Aux[A, ARepr],
bGen: LabelledGeneric.Aux[B, BRepr]): B = {
val baseHList = aGen.to(baseRecord)
val compositeHList = idField :: baseHList
bGen.from(compositeHList) //Type mismatch. Required BRepr, found F :: ARepr
}
}
Output:
Book: Book(Programming in Scala,Odersky, Spoon, Venners,2008)
PBook: PersistentBook(15,Programming in Scala,Odersky, Spoon, Venners,2008)
GenBook: 15 :: Programming in Scala :: Odersky, Spoon, Venners :: 2008 :: HNil
That's the closest i got so far is the injectFieldSimpleGeneric, but it returns an HList, not a B the objective is to be able to generate IDs for records so that i can insert them with self generated IDs when i try to expand it to produce a B, the HList to B is incompatible
There are two issues here. The first is that you haven't provided the compiler with any evidence that
AReprandBReprare related by some shared structure. You could do this by changing thebGenconstraint:This works:
And then:
Unfortunately you definitely don't want to have to provide all the type parameters every time you call this method, and the compiler can't infer them:
The problem is that even though you've given the compiler evidence that
AandBshare structure, you haven't told it how to pickB.Bdoesn't appear anywhere in the explicit arguments here, and the compiler's not going to enumerate all of the case classes in scope trying to find one with an appropriateLabelledGenericinstance.There are two ways you could resolve this issue. One would be to have some kind of type class like this:
And then provide instances like
HasEntity.Aux[Book, PersistentBook]for each pair of case classes. The other approach would be to rewrite yourinjectFieldGenericso that you can provide a single type parameter:And then:
Here you still have to specify the target, but the compiler will be able to verify that it's a valid match and to put together the required mapping.