Here is a solution using the same ideas as in my previous answer.
- doing minor transformations (Option to default value)
- ignore tail fields
Of course there are certain restrictions.
object HListsFlatten {
import shapeless.{::, HList, HNil}
sealed trait DefaultValue[V] {
def value: V
}
implicit val defaultInt: DefaultValue[Int] = new DefaultValue[Int] {
override def value = 0
}
sealed trait HConv[From <: HList, To <: HList] {
def convert(list: From): To
}
implicit def buildHConvNil: HConv[HNil, HNil] = new HConv[HNil, HNil] {
override def convert(list: HNil): HNil = HNil
}
implicit def buildHConvShorten[H <: AnyVal, T <: HList]
: HConv[::[H, T], ::[H, HNil]] = new HConv[::[H, T], ::[H, HNil]] {
override def convert(list: ::[H, T]): ::[H, HNil] = {
list.head :: HNil
}
}
implicit def buildHConvOption[H, T <: HList, T2 <: HList](
implicit conv: HConv[T, T2],
default: DefaultValue[H]): HConv[::[Option[H], T], ::[H, T2]] =
new HConv[::[Option[H], T], ::[H, T2]] {
override def convert(list: ::[Option[H], T]): ::[H, T2] = {
list.head.getOrElse(default.value) :: conv.convert(list.tail)
}
}
implicit def buildHConv[H <: AnyVal, T <: HList, T2 <: HList](
implicit conv: HConv[T, T2]): HConv[::[H, T], ::[H, T2]] =
new HConv[::[H, T], ::[H, T2]] {
override def convert(list: ::[H, T]): ::[H, T2] = {
list.head :: conv.convert(list.tail)
}
}
implicit def buildHConvString[T <: HList, T2 <: HList](
implicit conv: HConv[T, T2]): HConv[::[String, T], ::[String, T2]] =
new HConv[::[String, T], ::[String, T2]] {
override def convert(list: ::[String, T]): ::[String, T2] = {
list.head :: conv.convert(list.tail)
}
}
def flatten[A <: HList, B <: HList](list: A)(implicit conv: HConv[A, B]): B =
conv.convert(list)
}
Example:
import shapeless.Generic
case class Cat(color: Option[Int], isFat: Boolean, newField: String)
case class Kitten(color: Int, isFat: Boolean)
val cat = Cat(color = Some(3), isFat = true, "SomeValue")
val genCat = Generic[Cat]
val genKit = Generic[Kitten]
import HListsFlatten._
scala> val kitten = genKit.from(flatten(genCat.to(cat)))
kitten: Kitten = Kitten(3,true)