首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >自动将case类转换为无形状的可扩展记录?

自动将case类转换为无形状的可扩展记录?
EN

Stack Overflow用户
提问于 2015-11-22 18:50:48
回答 1查看 1K关注 0票数 7

如果我有这两个案例类:

代码语言:javascript
复制
case class Address(street : String, zip : Int)
case class Person(name : String, address : Address)

还有一个例子:

代码语言:javascript
复制
val person = Person("Jane", Address("street address", 12345))

有无形状的方法自动将person转换为可扩展的记录吗?

我对浅薄和深沉的转换都感兴趣。

较浅的副本应该类似于:

代码语言:javascript
复制
'name ->> "Jane" :: 'address ->> Address("street address", 12345) :: HNil

在深度转换中,嵌套的case类也成为记录:

代码语言:javascript
复制
'name ->> "Jane" :: 'address ->> ('street ->> "street address" :: 'zip ->> 12345 :: HNil) :: HNil

我还对将记录转换回案例类感兴趣。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-11-22 22:10:21

假设我们有以下设置:

代码语言:javascript
复制
import shapeless._, shapeless.labelled.{ FieldType, field }

case class Address(street: String, zip: Int)
case class Person(name: String, address: Address)

val person = Person("Jane", Address("street address", 12345))

type ShallowPersonRec =
  FieldType[Witness.`'name`.T, String] ::
  FieldType[Witness.`'address`.T, Address] :: HNil

type DeepPersonRec =
  FieldType[Witness.`'name`.T, String] ::
  FieldType[
    Witness.`'address`.T,
    FieldType[Witness.`'street`.T, String] ::
    FieldType[Witness.`'zip`.T, Int] :: HNil
  ] :: HNil

“无型”的LabelledGeneric直接支持浅壳:

代码语言:javascript
复制
val shallow: ShallowPersonRec = LabelledGeneric[Person].to(person)

或者如果您想要一个泛型助手方法:

代码语言:javascript
复制
def shallowRec[A](a: A)(implicit gen: LabelledGeneric[A]): gen.Repr = gen.to(a)

val shallow: ShallowPersonRec = shallowRec(person)

你可以和from一起回去

代码语言:javascript
复制
scala> val originalPerson = LabelledGeneric[Person].from(shallow)
originalPerson: Person = Person(Jane,Address(street address,12345))

深层次的情况要复杂得多,据我所知,没有任何方便的方法可以用无形提供的类型类和其他工具来实现这一点,但是您可以通过这个问题 (现在是一个无形状的测试用例 )对我的代码进行调整,以实现您想做的事情。首先用于类型类本身:

代码语言:javascript
复制
trait DeepRec[L] extends DepFn1[L] {
  type Out <: HList

  def fromRec(out: Out): L
}

然后是一个低优先级的实例,用于记录头本身没有LabelledGeneric实例的情况:

代码语言:javascript
复制
trait LowPriorityDeepRec {
  type Aux[L, Out0] = DeepRec[L] { type Out = Out0 }

  implicit def hconsDeepRec0[H, T <: HList](implicit
    tdr: Lazy[DeepRec[T]]
  ): Aux[H :: T, H :: tdr.value.Out] = new DeepRec[H :: T] {
    type Out = H :: tdr.value.Out    
    def apply(in: H :: T): H :: tdr.value.Out = in.head :: tdr.value(in.tail)
    def fromRec(out: H :: tdr.value.Out): H :: T =
      out.head :: tdr.value.fromRec(out.tail)
  }
}

然后是同伴对象的其余部分:

代码语言:javascript
复制
object DeepRec extends LowPriorityDeepRec {
  def toRec[A, Repr <: HList](a: A)(implicit
    gen: LabelledGeneric.Aux[A, Repr],
    rdr: DeepRec[Repr]
  ): rdr.Out = rdr(gen.to(a))

  class ToCcPartiallyApplied[A, Repr](val gen: LabelledGeneric.Aux[A, Repr]) {
    type Repr = gen.Repr    
    def from[Out0, Out1](out: Out0)(implicit
      rdr: Aux[Repr, Out1],
      eqv: Out0 =:= Out1
    ): A = gen.from(rdr.fromRec(eqv(out)))
  }

  def to[A](implicit
    gen: LabelledGeneric[A]
  ): ToCcPartiallyApplied[A, gen.Repr] =
    new ToCcPartiallyApplied[A, gen.Repr](gen) 

  implicit val hnilDeepRec: Aux[HNil, HNil] = new DeepRec[HNil] {
    type Out = HNil    
    def apply(in: HNil): HNil = in
    def fromRec(out: HNil): HNil = out
  }

  implicit def hconsDeepRec1[K <: Symbol, V, Repr <: HList, T <: HList](implicit
    gen: LabelledGeneric.Aux[V, Repr],
    hdr: Lazy[DeepRec[Repr]],
    tdr: Lazy[DeepRec[T]]
  ): Aux[FieldType[K, V] :: T, FieldType[K, hdr.value.Out] :: tdr.value.Out] =
    new DeepRec[FieldType[K, V] :: T] {
      type Out = FieldType[K, hdr.value.Out] :: tdr.value.Out
      def apply(
        in: FieldType[K, V] :: T
      ): FieldType[K, hdr.value.Out] :: tdr.value.Out =
        field[K](hdr.value(gen.to(in.head))) :: tdr.value(in.tail)
      def fromRec(
        out: FieldType[K, hdr.value.Out] :: tdr.value.Out
      ): FieldType[K, V] :: T =
        field[K](gen.from(hdr.value.fromRec(out.head))) ::
          tdr.value.fromRec(out.tail)
    }
}

(请注意,DeepRec特性和对象必须一起定义才能结合在一起。)

这是混乱的,但它起作用了:

代码语言:javascript
复制
scala> val deep: DeepPersonRec = DeepRec.toRec(person)
deep: DeepPersonRec = Jane :: (street address :: 12345 :: HNil) :: HNil

scala> val originalPerson = DeepRec.to[Person].from(deep)
originalPerson: Person = Person(Jane,Address(street address,12345))

转换回case类的to / from语法是必要的,因为任何给定的记录都可能对应大量潜在的case类,因此我们需要能够指定目标类型,而且由于Scala不支持部分应用的类型参数列表,所以我们必须将操作分成两部分(其中一部分将显式指定其类型,而另一部分的类型参数将被推断)。

票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33858745

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档