在隐含的 LabelledGeneric 中保留关键信息

retain key information in implicit LabelledGeneric

我有这段代码可以将 class 的任意大小转换为 HList

class Record[H <: HList](val hs: H)

object Record {
  import shapeless.LabelledGeneric
  def apply[P <: Product, L <: HList](p: P)(implicit gen: LabelledGeneric.Aux[P, L]) = new Record[L](gen.to(p))
}

我想按如下方式使用:

import shapeless._
import syntax.singleton._
val h = Record('a ->> 1, 'b ->> 2)
@ h.hs
res91: cmd90.<refinement>.this.type.Out = 1 :: 2 :: HNil

但是,转换后密钥不会保留。与创建相同的 HList 相反:

@ h.hs.keys //error: could not find implicit value for parameter keys: shapeless.ops.record.Keys[this.Out]
@ ('a ->> 1 :: 'b ->> 2 :: HNil).keys //works ok

问题是如何保留记录中的关键信息。

您使用的记录不正确。

'a ->> 1'b ->> 2这样的东西不应该单独使用,它们应该在HList里面使用,比如'a ->> 1 :: 'b ->> 2 :: HNil

当你写 Record('a ->> 1, 'b ->> 2) 时,Record 的参数被认为是 Tuple2。由于 Tuple2 是一个案例 class,LabelledGeneric 将其隐式转换为 HList(实际上是一个无形的记录)。看你的h,它的类型很奇怪(伪代码):

Record['_1 -> 'a -> Int :: '_2 -> 'b -> Int :: HNil]

_1_2Tuple2的字段名)。

所以如何修复你的代码取决于你真正想要得到的东西(我不确定)。一种可能的方法是

//  class Record[H <: HList](val hs: H)

object Record {
  def apply[P <: Product, L <: HList](p: P)(implicit gen: LabelledGeneric.Aux[P, L]) = /*new Record[L](*/gen.to(p)/*)*/
}

case class MyCaseClass(a: Int, b: Int)

val h = Record(MyCaseClass(1, 2))

h/*.hs*/   // 1 :: 2 :: HNil

h/*.hs*/.keys   // 'a :: 'b :: HNil

('a ->> 1 :: 'b ->> 2 :: HNil).keys   // 'a :: 'b :: HNil

I would like to have something like Record('a ->> 1, 'b ->> 2) ... or potentially Record(('a', 1), ('b', 2)) as an input, and an HList 'a ->> 1 :: 'b ->> 2 :: HNil as an output.

那你就不需要LabelledGeneric了。您可以将 Record('a ->> 1, 'b ->> 2) 转换为一个元组 ('a ->> 1, 'b ->> 2)(如果您定义了 case class Record(p: Product),则为 Record.unapply(...).get),然后 :

import shapeless.{::, HNil}
import shapeless.syntax.std.tuple._
import shapeless.syntax.singleton._
('a ->> 1, 'b ->> 2).productElements == 'a ->> 1 :: 'b ->> 2 :: HNil // true