【问题标题】:Converting case class to another recursively structural identical case class将案例类转换为另一个递归结构相同的案例类
【发布时间】:2016-08-05 22:07:03
【问题描述】:

我正在尝试使用 Shapeless 来转换这样的案例类:

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,我可以轻松地在AAnotherA 之间进行转换,但不适用于BAnotherB,因为(当然)a 成员属于不同类型。

我想我可以将 case 类转换为 this example 之后的嵌套 HList,但是如何将嵌套的 HList 转换回 case 类,或者有更直接的方法来做到这一点?

【问题讨论】:

    标签: scala shapeless


    【解决方案1】:

    这是DeepHUnlister 的一个可能实现,它递归地将泛型表示(嵌套HList)中的子Hlists 替换为其各自的案例类实例:

    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)
    

    【讨论】:

    • 第一个解决方案的第一个参数可以使用DeepHLister[C :: HNil].Out而不是CRepr,因此对于源类型可用的情况,这是完美的答案:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-07
    • 2018-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-09
    • 2016-07-27
    相关资源
    最近更新 更多