我正在尝试基于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
}
}
}
从我的角度来看,此解决方案应该可以,但仍然无法正常工作。 编译失败,并显示以下错误消息:
Error:(17, 41) lg2hl.Repr is not an HList type
val selector = Selector.mkSelector[labelledGeneric.Repr, FieldNameType, V]
为什么它抱怨lg2hl.Repr is not an HList type
?
Repr
在LGtoHL
中明确定义为type Repr <: HList
。
我的代码有什么问题?
非常感谢您的帮助。
答案 0 :(得分:2)
为什么lenses还不够?
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
类型
来自这里:https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/generic.scala#L511
u.baseType(HConsSym)
现在为NoType。
我猜测GenField[V](val fieldName: Symbol)
将无法工作,因为必须在编译时知道fieldName
中的Witness(fieldName)
。例如
lens[Foo] >> 'uhu
有效但是
val uhu: Witness.`'uhu`.T = 'uhu.narrow
lens[Foo] >> uhu
没有。这就是为什么通过宏实现镜头,见证,LabelledGeneric的原因。