I would like to transform an HList type parameter to a nested Map-type, e.g. Int :: String :: String :: HNil
should become Map[Int, Map[String, Map[String, T]]]]
where T would be another type parameter of the same function, like:
def somedef[T, L <: HList](t: T)(implicit f: ???): f.Out
where f.Out is T in case of HNil or a nested Map-structure with dept L.size
Is there any way this can be done?
I'm not aware of a standard thing to do such a transformation, but you could roll out your custom converter in the same way as various HList ops (like map
) are implemented inside shapeless (see trait Mapper
). The code could be something like this:
import scala.language.higherKinds
import scala.collection.immutable.Map
import shapeless._
sealed trait HListToMap[L <: HList, T] {
type Out
def convert(hlist: L, value: T): Out
}
object HListToMap {
// public interface
def wrap[L <: HList, T](value: T, keys: L)(implicit converter: HListToMap[L, T]): converter.Out =
converter.convert(keys, value)
// implementation details
type Aux[L <: HList, T, Out2] = HListToMap[L, T] { type Out = Out2 }
private trait Impl[L <: HList, T, Out2] extends HListToMap[L, T] {
override type Out = Out2
}
implicit def hnil[T]: Aux[HNil, T, T] = new Impl[HNil, T, T] {
override def convert(hlist: HNil, value: T): T = value
}
implicit def hnil2[T]: Aux[HNil.type, T, T] = new Impl[HNil.type, T, T] {
override def convert(hlist: HNil.type, value: T): T = value
}
implicit def recurse[H, L <: HList, T](implicit inner: HListToMap[L, T]): Aux[H :: L, T, Map[H, inner.Out]] = new Impl[H :: L, T, Map[H, inner.Out]] {
override def convert(hlist: H :: L, value: T): Map[H, inner.Out] = {
val im = inner.convert(hlist.tail, value)
Map(hlist.head -> im)
}
}
}
def test(): Unit = {
val keys = "abc" :: 1 :: 0.5 :: HNil
val value = "Xyz"
val m: Map[String, Map[Int, Map[Double, String]]] = HListToMap.wrap(value, keys)
println(m)
val just: String = HListToMap.wrap(value, HNil)
println(just)
}
You can see it online