将HList转换为另一个HList

时间:2019-12-10 22:25:43

标签: scala shapeless

我正在尝试通过转换为HList将一个案例类转换为另一个案例类。

df2 %>% 
    mutate(rn =row_number()) %>% 
    pivot_longer(cols = -rn, names_to = c(".value", "date"),
        names_sep="(?<=[a-z])(?=[0-9])") %>% 
    arrange(rn, date) %>% 
    group_by(rn) %>% 
    summarise_at(vars(zip:child), ~ last(.[!is.na(.)])) %>%
    ungroup %>%
    select(-rn) %>%
    rename_all( ~ str_c(., '_agg')) %>%
    bind_cols(df2, .)
#   zip20190601 zip20180401 zip20171001 zip20160801 income20190601 income20180401 income20171001 income20160801 child20160801 child20171001
#1           NA       11440       12016       15686          75038          63573          82391          47517             0             1
#2        13089       12626       13670       16155          89494          64984          62603          47252             0             1
#3        13258       12249       13333       16819             NA             NA          48231          45729             0             1
#4           NA          NA       18480       18611          89480          67348          55516          45863             0             1
#5        13990       10497       12573       13406          70053          63850          87833          48332             1             2
#6        17005       11491       15227       17518          78087          70741          46318          47823             1             2
#7        17174       17006       13461       11189          76780          66649          54578          46196             1             2
#8        12452       15317       18049       14284          76654          73583          70090          48281             0             1
#9        18449       14262       11013       17810          91422          79722          53948          45986             0             1
#10       11429       11731       13564       14603          84282          60190          45133          46956             0             1
#   child20180401 child20190601 zip_agg income_agg child_agg
#1              1             2   11440      75038         2
#2              1             2   13089      89494         2
#3              1             2   13258      48231         2
#4              1             2   18480      89480         2
#5              2             3   13990      70053         3
#6              2             3   17005      78087         3
#7              2             3   17174      76780         3
#8              1             2   12452      76654         2
#9              1             2   18449      91422         2
#10             1             2   11429      84282         2

我可以通过gem.to从Source转换为HList,通过gen.from从HList转换为Destination。 我为Source上的每种参数类型编写了一个Converter,以将其转换为Destination中的相应类型,但是我不确定如何递归遍历HList。我的尝试显示在下面的case class Source(p1:S1, p2:S2) -> HList[S1:+:S2] -> HList[D1:+:D2] ->case class Destination(p1:D1,p2:D2)

hlistEncoder

我使用这种方法来测试HList到HList的转换

trait Converter[T] {
  def convert(t:T): Datastructure
}
object Converter {
  implicit object StrDatastructure extends Converter[String]{
    def convert(t:String) = Datastructure.Str(t)
  }
  implicit object NumDatastructure extends Converter[Double]{
    def convert(t :Double) = Datastructure.Num(t)
  }
  implicit object IncDatastructure extends Converter[Inc]{
    def convert(t :Inc) = Datastructure.Incc(t)
  }
  implicit def SeqDatastructure[T: Converter]: Converter[Seq[T]] = new Converter[Seq[T]]{
    def convert(t: Seq[T]) = {
      Datastructure.Listt(t.map(implicitly[Converter[T]].convert):_*)
    }
  }

  //HList traversals

  implicit object hnilDatastructure extends Converter[HNil]{
    def convert(t: HNil) = Datastructure.Hnill(t)
  }

  implicit def hlistEncoder[H, T <: HList](implicit
                                            hEncoder: Converter[H],
                                            tEncoder: Converter[T]
                                          ): Converter[H :: T] = new Converter[H :: T] {
    def apply(h:H, t:T)= {
      case (h :: t) => hEncoder.convert(h) ++ tEncoder.convert(t)
    }

  }

}

有什么想法如何实现def convertToDatastructureN[T](x: T)(implicit converter: Converter[T]): Datastructure = { converter.convert(x) } case class Inc(i:Int) case class Source(i: Int, n:Inc) val x = Generic[Source] val xHlist = x.to(Source(99, Inc(5))) convertToDatastructureN(xHlist)

1 个答案:

答案 0 :(得分:3)

我想你有

sealed trait Datastructure
object Datastructure {
  case class Str(t: String) extends Datastructure
  case class Num(t: Double) extends Datastructure
  case class Incc(t: Inc) extends Datastructure
  case class Listt(t: Datastructure*) extends Datastructure
  case class Hnill(t: HNil) extends Datastructure
}

您希望您的类型类ConverterT转换为Datastructure。而且(HList[S1:+:S2] -> HList[D1:+:D2],我想应该用::代替:+:),您还希望将HList的子类型转换为HList的子类型(而不是HList本身,因为否则Generic无法还原案例类)。所以要么修改您的类型类

trait Converter[T] {
  type Out
  def convert(t:T): Out
}

或者您需要两个类型类:原始的Converter

trait HListConverter[T <: HList] {
  type Out <: HList
  def convert(t:T): Out
}

此外,当前您的Converter很粗糙。它将每个T转换为Datastructure而不是Datastructure的特定子类型。这意味着Generic将仅能还原形状的案例类

MyClass(x: Datastructure) 
MyClass(x: Datastructure, y: Datastructure) 
...

这真的是您想要的吗?如果是这样,那么还可以,如果不需要,则需要

MyClass(x: Str) 
MyClass(x: Num, y: Incc) 
...

然后又需要

trait Converter[T] {
  type Out
  def convert(t:T): Out
}

您可以使用标准的HListConverter代替shapeless.ops.hlist.Mapper