Бесформенный Mapper для LabelledGeneric не найден
У меня есть базовый пул типов, определенный так:
sealed trait Section
final case class Header(...) extends Section
final case class Customer(...) extends Section
final case class Supplier(...) extends Section
final case class Tech(...) extends Section
Я хотел бы представить некоторые классы дел, состоящие из типов из этого пула, например:
final case class ContractViewPartners(customer: Customer, supplier: Supplier)
final case class ContractView(header: Header, partners: ContractViewPartners, tech: Tech)
Поскольку они будут интенсивно использоваться в генераторах функций, реализованных через HList
используя метод, описанный здесь, я хотел бы убедиться, что каждое поле представленного типа является одним из
Section
подтипHList
изSection
подтипы- запись презентабельной как
HList
изSection
подтипы
Я определил простую проверку времени компиляции для этого условия:
object traverseView extends Poly1 {
implicit def caseSection[S <: Section] = at[S](_ => ())
implicit def caseSectionList[L <: HList]
(implicit evt: ToTraversable.Aux[L, List, Section]) = at[L](_ => ())
implicit def caseRecord[R, L <: HList]
(implicit lgen: LabelledGeneric.Aux[R, L],
trav: ToTraversable.Aux[L, List, Section]) = at[R](_ => ())
}
private def contractViewIsMultiSection(v: ContractView) = {
val gen = LabelledGeneric[ContractView].to(v)
gen map traverseView
}
Но это не с (имена пакетов удалены)
не удалось найти неявное значение для преобразователя параметров: Mapper [traverseView.type,:: [Заголовок с KeyTag[Символ с тегом [String("header")], Заголовок],::[ContractViewPartners с KeyTag[Символ с тегом [String("partners")],ContractViewPartners],::[Tech with KeyTag[Символ с тегом [String("tech")],Tech],HNil]]]]
Если я удалю partners
раздел из ContractView
это работает, и если я пытаюсь решить implicits
на ContractViewPartners
они тоже будут найдены.
Опять же при написании вопроса я нашел решение с добавлением .values
как это
private def contractViewIsMultiSection(v: ContractView) = {
val gen = LabelledGeneric[ContractView].to(v)
.values //!!!
gen map traverseView
}
Это может быть тот тип with KeyTag[...]
не работает как источник LabelledGeneric
трансформация?
1 ответ
Проблема в том, что Case
инвариантен, поэтому тот факт, что у вас есть Case
экземпляр для ContractViewPartners
не означает, что у вас есть дело для ContractViewPartners
с меткой уровня типа (которая является только подтипом ContractViewPartners
). Вы можете исправить это довольно просто, генерируя экземпляры, например, для FieldType[K, ContractViewPartners]
(для некоторых произвольных K
):
sealed trait Section
final case class Header(s: String) extends Section
final case class Customer(s: String) extends Section
final case class Supplier(s: String) extends Section
final case class Tech(s: String) extends Section
final case class ContractViewPartners(customer: Customer, supplier: Supplier)
final case class ContractView(header: Header, partners: ContractViewPartners, tech: Tech)
import shapeless._, labelled.FieldType, ops.hlist.ToList
object traverseView extends Poly1 {
implicit def caseSection[S <: Section] = at[S](_ => ())
implicit def caseSectionList[K, L <: HList](implicit
lub: ToList[L, Section]
) = at[FieldType[K, L]](_ => ())
implicit def caseRecord[K, C, L <: HList](implicit
gen: Generic.Aux[C, L],
lub: ToList[L, Section]
) = at[FieldType[K, C]](_ => ())
}
private def contractViewIsMultiSection(v: ContractView) = {
val gen = LabelledGeneric[ContractView].to(v)
gen map traverseView
}
Вы также можете просто использовать Generic[ContractView]
в contractViewIsMultiSection
если вы не заботитесь о ярлыках.
Я бы, вероятно, предложил не использовать Poly1
для такого рода вещей, хотя. Если вам просто нужны доказательства того, что типы являются правильными, вы можете сделать это немного более аккуратно с помощью пользовательского класса типов.