Dynamically create expandable recording in formless version 2.0

I need to create an extensible record with the HList key and a map of values, here is the MWE of what I'm trying to achieve (you can copy / paste this into any REPL with formless 2.0 available to reproduce the problem)

 import shapeless._; import syntax.singleton._; import record._ case class Foo[T](column: Symbol) val cols = Foo[String]('column1) :: HNil val values = Map("column1" -> "value1") object toRecord extends Poly1 { implicit def Foo[T] = at[Foo[T]] { foo => val k = foo.column.name val v = values.get(k) (k ->> v) } } val r = cols.map(toRecord) // r: shapeless.::[Option[String] with shapeless.record.KeyTag[k.type,Option[String]] forSome { val k: String },shapeless.HNil] = Some(value1) :: HNil val value = r("column1") // error: No field String("column1") in record shapeless.::[Option[String] with shapeless.record.KeyTag[k.type,Option[String]] forSome { val k: String },shapeless.HNil] val value = r("column1") 

If I try to determine the record manually, everything will work as expected

  val q = ("column1" ->> Some("value1")) :: HNil // q: shapeless.::[Some[String] with shapeless.record.KeyTag[String("column1"),Some[String]],shapeless.HNil] = Some(value1) :: HNil q("column1") // Some[String] = Some(value1) 

Clearly, the difference is that in one case, the KeyTag is of type

 KeyTag[String("column1"), Some[String]] 

and in a (non-working) friend

 KeyTag[k.type,Option[String]] forSome { val k: String } 

I feel the problem is that the string k not statically known, but I don't know how to fix it. Generally speaking, is there a way to dynamically generate an expandable entry from a list of keys?

I am afraid that the answer will be to use a macro, but I would be glad if another solution existed.

+8
scala shapeless
source share
1 answer

This is not so bad if you can slightly modify the definition of Foo so that it can track the singleton type of the column key (note that I removed the unused parameter of type T ):

 import shapeless._; import syntax.singleton._; import record._ case class Foo[K <: Symbol](column: Witness.Aux[K]) val cols = Foo('column1) :: HNil val values = Map("column1" -> "value1") object toRecord extends Poly1 { implicit def atFoo[K <: Symbol] = at[Foo[K]] { foo => field[K](values.get(foo.column.value.name)) } } val r = cols.map(toRecord) 

And then:

 scala> val value = r('column1) value: Option[String] = Some(value1) 

Please note that I changed my string key ( "column1" ) to a character, since we entered this into the record.

+7
source share

All Articles