If we ignore the similarities in field names and focus on types, we can do the following:
def alignRecordByTypeInsideConstructor[A, B, L1 <: HList, L2 <: HList,
F[_], G[_], L3 <: HList, L4 <: HList, Out <: HList](a: A, b: B, f: F ~> Id, g: Id ~> G)(
implicit
gen1: Generic.Aux[A, L1],
gen2: Generic.Aux[B, L2],
comapped1: Comapped.Aux[L1, F, L3],
natTRel1: NatTRel[L1, F, L3, Id],
comapped2: Comapped.Aux[L2, G, L4],
natTRel2: NatTRel[L2, G, L4, Id],
align: Align[L3, L4],
mapped: Mapped.Aux[L4, G, Out],
natTRel3: NatTRel[L4, Id, Out, G]
): Out =
alignByTypeInsideConstructor[L1, L2, F, G, L3, L4, Out](gen1.to(a), gen2.to(b), f, g)
def alignByTypeInsideConstructor[L1 <: HList, L2 <: HList,
F[_], G[_], L3 <: HList, L4 <: HList, Out <: HList](l1: L1, l2: L2, f: F ~> Id, g: Id ~> G)(
implicit
comapped1: Comapped.Aux[L1, F, L3],
natTRel1: NatTRel[L1, F, L3, Id],
comapped2: Comapped.Aux[L2, G, L4],
natTRel2: NatTRel[L2, G, L4, Id],
align: Align[L3, L4],
mapped: Mapped.Aux[L4, G, Out],
natTRel3: NatTRel[L4, Id, Out, G]): Out =
natTRel3.map(g, align(natTRel1.map(f, l1)))
object columnToId extends (Column ~> Id) {
override def apply[A](column: Column[A]): A = column match {
case Column(a) => a
}
}
object idToOption extends (Id ~> Option) {
override def apply[A](a: A): Option[A] = Some(a)
}
alignByTypeInsideConstructor(
Column(1L) :: Column(2) :: Column("a") :: HNil,
Option(3L) :: Option("b") :: Option(4) :: HNil,
columnToId,
idToOption
)
//Some(1) :: Some(a) :: Some(2) :: HNil
alignRecordByTypeInsideConstructor(
DTable(Column(1L), Column(2), Column("a")),
DFilter(Option(3L), Option("b"), Option(4)),
columnToId,
idToOption
)
//Some(1) :: Some(a) :: Some(2) :: HNil