Scala 如何使用LabelledGeneric更新案例类字段?
使用shapeless,可以使用Scala 如何使用LabelledGeneric更新案例类字段?,scala,shapeless,Scala,Shapeless,使用shapeless,可以使用LabelledGeneric更新案例类字段,如下所示: case class Test(id: Option[Long], name: String) val test = Test(None, "Name") val gen = LabelledGeneric[Test] scala> gen.from(gen.to(test) + ('id ->> Option(1L))) res0: Test = Test(Some(1),Name)
LabelledGeneric
更新案例类字段,如下所示:
case class Test(id: Option[Long], name: String)
val test = Test(None, "Name")
val gen = LabelledGeneric[Test]
scala> gen.from(gen.to(test) + ('id ->> Option(1L)))
res0: Test = Test(Some(1),Name)
我希望测试
类(和其他类)扩展一个抽象类模型
,该类将实现一个id为的方法,该方法将使用类似于上述代码的标签来更新id
字段,如果它有一个字段(应该有)
我的尝试是向模型
的构造函数添加一个LabelledGeneric[a]
的隐式参数,该参数很好地具体化了。我还需要以某种方式为记录语法提供证据,LabelledGeneric#Repr
具有要替换的id
字段。将隐式的更新程序
参数添加到withId
可以满足编译器的要求,因此下面的代码可以编译,但不可用
import shapeless._, record._, ops.record._, labelled._, syntax.singleton._, tag._
abstract class Model[A](implicit gen: LabelledGeneric[A] { type Repr <: HList }) { this: A =>
def id: Option[Long]
val idWitness = Witness("id")
type F = FieldType[Symbol with Tagged[idWitness.T], Option[Long]]
def withId(id: Long)(implicit u: Updater.Aux[gen.Repr, F, gen.Repr]) =
gen.from(gen.to(this) + ('id ->> Option(id)))
}
case class Test(id: Option[Long], name: String) extends Model[Test]
这是在使用shapeless 2.3,尽管在2.2中由于不同的原因失败了(似乎Updater
有一个大的重构)
是否有可能通过不成形来实现这一点,或者我偏离了目标?这里的主要问题是LabelledGeneric(Repr
)的优化结果类型丢失。在Model
上,关于Repr
的唯一已知信息是Repr
scala> weakTypeOf[test.gen.Repr].baseType(weakTypeOf[::[_, _]].typeConstructor.typeSymbol)
res12: reflect.runtime.universe.Type = <notype>
object Model {
private type IdField = Symbol with Tagged[Witness.`"id"`.T]
private val IdField = field[IdField]
type F = FieldType[IdField, Option[Long]]
}
abstract class Model[A] { this: A =>
import Model._
def id: Option[Long]
def withId[L <: HList](id: Long)(implicit // L captures the fully refined `Repr`
gen: LabelledGeneric.Aux[A, L], // <- in here ^
upd: Updater.Aux[L, F, L] // And can be used for the Updater
): A = {
val idf = IdField(Option(id))
gen.from(upd(gen.to(this), idf))
}
}
case class Test(id: Option[Long], name: String) extends Model[Test]
case class Test(id: Option[Long], name: String) extends Model[Test]
object Test {
implicit val gen = LabelledGeneric[Test]
}
val test = Test(None, "Name")
println("test.withId(12) = " + test.withId(12))
println("test.withId(12).withId(42) = " + test.withId(12).withId(42))