Здесь есть две проблемы. Во-первых, вы не предоставили компилятору никаких доказательств того, что ARepr
и BRepr
связаны некоторой общей структурой. Вы можете сделать это, изменив ограничение bGen
:
import shapeless._, shapeless.labelled.{FieldType, field}
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 }
def injectFieldGeneric[A, ARepr <: HList, B <: Entity, F <: B#Id](baseRecord: A, idField: F)(
implicit aGen: LabelledGeneric.Aux[A, ARepr],
bGen: LabelledGeneric.Aux[B, FieldType[Witness.`'id`.T, F] :: ARepr]
): B = {
val baseHList = aGen.to(baseRecord)
val compositeHList = field[Witness.`'id`.T](idField) :: baseHList
bGen.from(compositeHList)
}
Это работает:
val bookGen = LabelledGeneric[Book]
val scalaBook = Book("Programming in Scala", "Odersky, Spoon, Venners", 2008)
val persistedScalaBook =
injectFieldGeneric[Book, bookGen.Repr, PersistentBook, Long](scalaBook, 16L)
А затем:
scala> println(persistedScalaBook)
PersistentBook(16,Programming in Scala,Odersky, Spoon, Venners,2008)
К сожалению, вы определенно не понимаете Не нужно указывать все параметры типа каждый раз, когда вы вызываете этот метод, и компилятор не может их определить:
scala> val persistedScalaBook = injectFieldGeneric(scalaBook, 16L)
^
error: inferred type arguments [Book,Nothing,Nothing,Long] do not conform to method injectFieldGeneric's type parameter bounds [A,ARepr <: shapeless.HList,B <: Entity,F <: B#Id]
^
error: type mismatch;
found : Book
required: A
^
error: type mismatch;
found : Long(16L)
required: F
^
error: could not find implicit value for parameter aGen: shapeless.LabelledGeneric.Aux[A,ARepr]
Проблема в том, что даже если вы предоставили компилятору доказательства того, что 1016 * и B
структура обмена, вы не сказали ей, как выбрать B
. B
здесь нигде не фигурирует в явных аргументах, и компилятор не собирается перечислять все классы case в области видимости, пытаясь найти один с соответствующим LabelledGeneric
экземпляром.
Есть два способа Вы можете решить эту проблему. Можно было бы иметь некоторый класс типов, подобный этому:
trait HasEntity[A] { type E }
object HasEntity { type Aux[A, E0] = HasEntity[A] { type E = E0 } }
, а затем предоставлять экземпляры, подобные HasEntity.Aux[Book, PersistentBook]
, для каждой пары классов дел. Другой подход состоит в том, чтобы переписать ваш injectFieldGeneric
, чтобы вы могли предоставить один параметр типа:
class PartiallyAppliedInject[B <: Entity] {
type IdK = Witness.`'id`.T
def apply[A, ARepr <: HList, F <: B#Id, BRepr <: HList](baseRecord: A, idField: F)(
implicit aGen: LabelledGeneric.Aux[A, ARepr],
bGen: LabelledGeneric.Aux[B, FieldType[IdK, F] :: ARepr]
): B = {
val baseHList = aGen.to(baseRecord)
val compositeHList = field[IdK](idField) :: baseHList
bGen.from(compositeHList)
}
}
def injectFieldGeneric[B <: Entity]: PartiallyAppliedInject[B] =
new PartiallyAppliedInject[B]
И затем:
scala> val persistedScalaBook = injectFieldGeneric[PersistentBook](scalaBook, 16L)
persistedScalaBook: PersistentBook = PersistentBook(16,Programming in Scala,Odersky, Spoon, Venners,2008)
Здесь вам все еще нужно указать цель , но компилятор сможет проверить, что это допустимое совпадение, и собрать необходимое отображение.