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通过宏实现的原因。我更新了我的答案。我更新了我的答案。