Adrian
Adrian

Reputation: 5681

transform HList into another HList

I'm trying to convert a case class into another via conversion to HList.

case class Source(p1:S1, p2:S2) -> HList[S1:+:S2] -> HList[D1:+:D2] ->case class Destination(p1:D1,p2:D2)

I can convert from Source to HList via gem.to and from HList to Destination via gen.from. I wrote a Converter for each type of parameter on Source to convert it to its corresponding type in Destination but I am unsure how to recursively traverse the HList. My attempt is shown below in hlistEncoder

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)
    }

  }

}

I use this method to test HList to HList conversion

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)

Any ideas how to implement hlistEncoder?

Upvotes: 2

Views: 73

Answers (1)

Dmytro Mitin
Dmytro Mitin

Reputation: 51723

I guess you have

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
}

You want your type class Converter to transform T into Datastructure. But also (HList[S1:+:S2] -> HList[D1:+:D2], where I guess :: should be instead of :+:) you want subtypes of HList to be transformed into subtypes of HList (not into HList itself since otherwise Generic can't restore case class). So either you should modify your type class

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

or you need two type classes: your original Converter and

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

Moreover, currently your Converter is pretty rough. It transforms every T into Datastructure instead of into specific subtypes of Datastructure. This means Generic will be able to restore case classes only of shapes

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

Is it really what you want? If so then ok, if not and you need

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

then again you need

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

Instead of HListConverter you can use standard shapeless.ops.hlist.Mapper.

Upvotes: 3

Related Questions