Преобразование класса case в другой рекурсивно структурно идентичный класс case

Я пытаюсь использовать Shapeless для преобразования класса case следующим образом:

case class A(int: Int, str: String)
case class B(a: A, str: String)

case class AnotherA(int: Int, str: String)
case class AnotherB(a: AnotherA, str: String)

Используя Generic, я могу легко конвертировать между A и AnotherA, но не для B и AnotherB, потому что (конечно) член a имеет другой тип.

Я думаю, что могу преобразовать класс case во вложенный HList, следуя этот пример, но как преобразовать вложенный HList обратно в класс case или есть более простой способ сделать это?


person Chikei    schedule 04.08.2016    source источник


Ответы (1)


Вот возможная реализация DeepHUnlister, которая рекурсивно заменяет вложенные Hlist в общем представлении (вложенные HList) соответствующими экземплярами класса case:

trait DeepHUnlister[L <: HList, R <: HList] extends (L => R)

object DeepHUnlister {

  implicit object hnil extends DeepHUnlister[HNil, HNil] {
    def apply(r: HNil) = HNil
  }

  implicit def headList[H <: HList, T <: HList, HR <: HList, TR <: HList, A](
    implicit
      gen: Generic.Aux[A, HR],
      dhh: Lazy[DeepHUnlister[H, HR]],
      dht: Lazy[DeepHUnlister[T, TR]]):
        DeepHUnlister[H :: T, A :: TR] =
    new DeepHUnlister[H :: T, A :: TR] {
      def apply(r: H :: T) = gen.from(dhh.value(r.head)) :: dht.value(r.tail)
    }

  implicit def headAtomic[H, T <: HList, TR <: HList](
    implicit dht: Lazy[DeepHUnlister[T, TR]]):
      DeepHUnlister[H :: T, H :: TR] =
    new DeepHUnlister[H :: T, H :: TR] {
      def apply(r: H :: T) = r.head :: dht.value(r.tail)
    }

  def apply[T <: HList, R <: HList](implicit du: DeepHUnlister[T, R]):
    DeepHUnlister[T, R] = du

}

Пример:

case class A(int: Int, str: String)
case class B(a: A, str: String)
case class C(b: B, d: Double)

case class A1(int: Int, str: String)
case class B1(a: A1, str: String)
case class C1(a: B1, d: Double)

type ARepr = Int :: String :: HNil
type BRepr = ARepr :: String :: HNil
type CRepr = BRepr :: Double :: HNil

val c = C(B(A(1, "a"), "b"), 1.0)

val lister = DeepHLister[C :: HNil]
val repr = lister(c :: HNil)
println(repr)

val unlister = DeepHUnlister[CRepr :: HNil, C1 :: HNil]
val c1 = unlister(repr).head
// c1 = C1(B1(A1(1,a),b),1.0)

Возможно, это решение можно улучшить, чтобы избежать передачи типа представления в качестве параметра в unlister.


Обновить

Вот версия, в которой отсутствует исходный тип, но, к сожалению, требуется приведение:

trait DepInvFn1[T] {
  type In
  def apply(i: In): T
}

trait DeepHUnlister[R <: HList] extends DepInvFn1[R] { type In <: HList }

trait DeepHUnlisterLP {

  type Aux[L <: HList, R <: HList] = DeepHUnlister[R] { type In = L }

  implicit def headAtomic[H, TR <: HList](
    implicit dt: Lazy[DeepHUnlister[TR]]):
      Aux[H :: dt.value.In, H :: TR] =
    new DeepHUnlister[H :: TR] {
      type In = H :: dt.value.In
      def apply(r: H :: dt.value.In) = r.head :: dt.value(r.tail)
    }

}

object DeepHUnlister extends DeepHUnlisterLP {

  implicit object hnil extends DeepHUnlister[HNil] {
    type In = HNil
    def apply(r: HNil) = HNil
  }

  implicit def headList[HR <: HList, TR <: HList, A](
    implicit
      gen: Generic.Aux[A, HR],
      dh: Lazy[DeepHUnlister[HR]],
      dt: Lazy[DeepHUnlister[TR]]):
        Aux[dh.value.In :: dt.value.In, A :: TR] =
    new DeepHUnlister[A :: TR] {
      type In = dh.value.In :: dt.value.In
      def apply(r: dh.value.In :: dt.value.In) = gen.from(dh.value(r.head)) :: dt.value(r.tail)
    }

  def apply[R <: HList](implicit du: DeepHUnlister[R]): DeepHUnlister[R] = du

}

Применение:

val unlister = DeepHUnlister[C1 :: HNil]
val c1 = unlister(repr.asInstanceOf[unlister.In]).head
// c1 = C1(B1(A1(1,a),b),1.0)
person devkat    schedule 05.08.2016
comment
Первый параметр первого решения может использовать DeepHLister[C :: HNil].Out вместо CRepr, поэтому для ситуации доступен тип источника, это идеальный ответ :) - person Chikei; 09.08.2016