Бесформенный 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 для такого рода вещей, хотя. Если вам просто нужны доказательства того, что типы являются правильными, вы можете сделать это немного более аккуратно с помощью пользовательского класса типов.

Другие вопросы по тегам