将案例类转换为另一个递归结构相同的案例类 [英] Converting case class to another recursively structural identical case class
问题描述
我正在尝试使用 Shapeless 来转换这样的 case 类:
I am trying to use Shapeless to convert case class like these:
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
成员是不同的类型.
Using Generic
I can easily convert between A
and AnotherA
, but not for B
and AnotherB
because (of course) the a
member is of different type.
我想我可以按照 这个例子,但是如何将嵌套的 HList
转换回 case 类或者有更直接的方法它吗?
I think I can convert case class into a nested HList
following this example, but how do I convert the nested HList
back to case class or there is a more straightforward way to do it ?
推荐答案
这是一个 DeepHUnlister
的可能实现,它递归地替换通用表示中的子 Hlist
(嵌套 HList
) 及其各自的案例类实例:
Here's a possible implementation of a DeepHUnlister
which recursively replaces the sub-Hlist
s within the generic representation (nested HList
) with their respective case class instances:
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.
Maybe it's possible to enhance this solution to avoid passing the representation type as parameter to the unlister.
更新
这是一个省略源类型的版本,但不幸的是需要强制转换:
Here's a version that omits the source type, but unfortunately requires a cast:
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)
这篇关于将案例类转换为另一个递归结构相同的案例类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!