I have basic type pool defined like that:
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
I'd like to present some case classes composed of types from this pool like this:
final case class ContractViewPartners(customer: Customer, supplier: Supplier)
final case class ContractView(header: Header, partners: ContractViewPartners, tech: Tech)
As they would be heavily used in feature-generators implemented via transfoming to HList
s using method described here, I'd like to ensure that each field of presented type is one of
Section
subtypeHList
ofSection
subtypes- record presentable as
HList
ofSection
subtypes
I've defined simple compile-time checker for this condition:
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
}
But it fails with (package names removed)
could not find implicit value for parameter mapper: Mapper[traverseView.type,::[Header with KeyTag[Symbol with Tagged[String("header")],Header],::[ContractViewPartners with KeyTag[Symbol with Tagged[String("partners")],ContractViewPartners],::[Tech with KeyTag[Symbol with Tagged[String("tech")],Tech],HNil]]]]
If i remove partners
section from ContractView
it's working and if i try to resolve implicits
on ContractViewPartners
they will be found too.
Again while writing question i've found solution with adding .values
like that
private def contractViewIsMultiSection(v: ContractView) = {
val gen = LabelledGeneric[ContractView].to(v)
.values //!!!
gen map traverseView
}
Could it be that type with KeyTag[...]
is not working properly as source for LabelledGeneric
transformation?
The problem is that
Case
is invariant, so the fact that you have aCase
instance forContractViewPartners
doesn't mean that you have a case instance forContractViewPartners
with a type-level label (which is only a subtype ofContractViewPartners
). You can fix this pretty straightforwardly by generating instances for e.g.FieldType[K, ContractViewPartners]
(for some arbitraryK
):You could also just use
Generic[ContractView]
incontractViewIsMultiSection
if you don't care about the labels.I would probably suggest not using
Poly1
for this kind of thing, though. If you just want evidence that the types are right, you could do that a little more cleanly with a custom type class.