Seamless alignment with different types, but with the same labels

Does anyone know how to align two records when they have the same field names, but not the same value, one has a value wrapped in a column, another wrapped in Option and I need to bring them in a different order

import shapeless._
import shapeless.labelled.{FieldType, field}
import shapeless.ops.hlist.{Align, ToTraversable, ZipWith}

object ShapelessTest {


  def zipClasses[A, B, P <: Poly2, ARepr <: HList, BRepr <: HList, R <: HList, X]
  (a: A, b: B, f: P)(
    implicit
    aGen : LabelledGeneric.Aux[A, ARepr],
    bGen : LabelledGeneric.Aux[B, BRepr],
//    align : Align[ARepr, BRepr],
    zipWith : ZipWith.Aux[ARepr, BRepr, P, R],
    toTrav: ToTraversable.Aux[R, List, X]
  ): List[X] =
    aGen.to(a).zipWith(bGen.to(b))(f).toList
//    align.apply(aGen.to(a)).zipWith(bGen.to(b))(f).toList



  def main(args: Array[String]): Unit = {


    case class Column[A](value: A)

    case class DTable(id: Column[Long], s2: Column[Int], s: Column[String])
    case class DFilter(id: Option[Long], s: Option[String], s2: Option[Int])

    object filter extends Poly2 {
      implicit def repr[K <: Symbol, V] = at[FieldType[K, Column[V]], FieldType[K, Option[V]]] { (a, b) =>
        field[K]( (a, b).asInstanceOf[(Any, Any)] )
      }
    }

    val dTable = new DTable(Column(1L), Column(3), Column("s"))
    val dFilter = new DFilter(Option(222L), Option("second"), Option(234))


//    def filter[A](col: Column[A], filterCriteria: Option[A]): Option[Boolean] = ???

//    def filterClass(table: DTable, filter: DFilter): List[Option[Boolean]] = ???
    def filterClass(t: DTable, f: DFilter): List[(Any, Any)] = zipClasses(t, f, filter)

    val res = filterClass(dTable, dFilter)
    println(res)

  }

}

I can align two records with exactly the same types, and I can do zipWith with two records, even if the type is not exact, but they must be keyboard aligned.

thank

+4
source share
1 answer

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
+2
source

Source: https://habr.com/ru/post/1684385/


All Articles