Scala 由Shapeless驱动的通用字段访问器

Scala 由Shapeless驱动的通用字段访问器,scala,shapeless,Scala,Shapeless,我试图实现一个基于LabelledGeneric的方便的通用字段访问器。 用法应该如下所示: case class Foo(aha: String, uhu: Double, ehe: Int) case class Bar(uhu: Double, ahu: Boolean) val foo: Foo = ??? val bar: Bar = ??? val uhuGenField = new GenField('uhu) val uhuFooAccess = uhuGenField.fr

我试图实现一个基于
LabelledGeneric
的方便的通用字段访问器。 用法应该如下所示:

case class Foo(aha: String, uhu: Double, ehe: Int)
case class Bar(uhu: Double, ahu: Boolean)

val foo: Foo = ???
val bar: Bar = ???

val uhuGenField = new GenField('uhu)
val uhuFooAccess = uhuGenField.from[Foo]
val uhuBarAccess = uhuGenField.from[Bar]

def someFunWithUhu[X](xs: Seq[X], access: uhuGenField.Access[X]) = ??? 
我花了一些时间试图弄明白如何实现这种行为。 最终我想到了这个方法:

import shapeless._
import shapeless.ops.record.Selector

final class GenField[V](val fieldName: Symbol) {
  val fieldWitness = Witness(fieldName)
  type FieldNameType = fieldWitness.T

  trait Access[C] {
    def get(c: C): V
  }

  def from[C](implicit lg2hl: LGtoHL[C]): Access[C] = new Access[C] {
    override def get(c: C): V = {
      val labelledGeneric = lg2hl.labelledGeneric
      val selector = Selector.mkSelector[labelledGeneric.Repr, FieldNameType, V]
      selector(labelledGeneric.to(c))
    }
  }
}

// I need something like this to enable syntax like
//   genField.from[DesiredClass]
// i.e. to "latch" Repr inside a single instance
// and to don't pass it explicitly to `from` method. 
sealed trait LGtoHL[A] {
  type Repr <: HList
  val labelledGeneric: LabelledGeneric.Aux[A, Repr]
}

object LGtoHL {
  implicit def mkLGtoHL[A, ARepr <: HList](implicit lg: LabelledGeneric.Aux[A, ARepr]): LGtoHL[A] = {
    new LGtoHL[A] {
      override type Repr = ARepr
      override val labelledGeneric: LabelledGeneric.Aux[A, Repr] = lg
    }
  }
}
为什么它抱怨lg2hl.Repr不是HList类型?
Repr
LGtoHL
中明确定义为
type Repr为什么还不够

import shapeless.{Lens, lens}

case class Foo(aha: String, uhu: Double, ehe: Int)
case class Bar(uhu: Double, ahu: Boolean)

val foo: Foo = Foo("a", 1.0, 2)
val bar: Bar = Bar(3.0, true)

val fooUhu: Lens[Foo, Double] = lens[Foo] >> 'uhu
val barUhu: Lens[Bar, Double] = lens[Bar] >> 'uhu

fooUhu.get(foo) // 1.0
barUhu.get(bar) // 3.0

错误消息

lg2hl.Repr
不是
HList
类型

来自这里:

u.baseType(HConsSym)
现在是

我猜
GenField[V](val fieldName:Symbol)
将不起作用,因为
Witness(fieldName)
中的
fieldName
必须在编译时已知。比如说

lens[Foo] >> 'uhu
工作但是

val uhu: Witness.`'uhu`.T = 'uhu.narrow
lens[Foo] >> uhu
没有。这就是为什么透镜、见证器、LabelledGeneric通过宏实现的原因。

为什么还不够

import shapeless.{Lens, lens}

case class Foo(aha: String, uhu: Double, ehe: Int)
case class Bar(uhu: Double, ahu: Boolean)

val foo: Foo = Foo("a", 1.0, 2)
val bar: Bar = Bar(3.0, true)

val fooUhu: Lens[Foo, Double] = lens[Foo] >> 'uhu
val barUhu: Lens[Bar, Double] = lens[Bar] >> 'uhu

fooUhu.get(foo) // 1.0
barUhu.get(bar) // 3.0

错误消息

lg2hl.Repr
不是
HList
类型

来自这里:

u.baseType(HConsSym)
现在是

我猜
GenField[V](val fieldName:Symbol)
将不起作用,因为
Witness(fieldName)
中的
fieldName
必须在编译时已知。比如说

lens[Foo] >> 'uhu
工作但是

val uhu: Witness.`'uhu`.T = 'uhu.narrow
lens[Foo] >> uhu

没有。这就是为什么镜头、证人、LabelledGeneric通过宏实现的原因。

我更新了我的答案。我更新了我的答案。