gpt4 book ai didi

scala - 如何使用 LabelledGeneric 一般更新案例类字段?

转载 作者:行者123 更新时间:2023-12-02 04:12:48 24 4
gpt4 key购买 nike

使用无形,可以使用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)

我想要Test类(和其他)来扩展抽象类 Model ,这将实现一个方法 withId这将使用 LabelledGeneric类似上面的代码更新id字段,它应该有一个(它应该)。

我尝试添加 LabelledGeneric[A] 的隐式参数到 Model 的构造函数,这很好地实现了。我还需要以某种方式为 LabelledGeneric#Repr 的记录语法提供证据。有id要替换的字段。添加隐式Updater参数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]

调用test.withId(1)时,隐式Updater未能实现。宏报告 gen.Repr不是HList类型,实际上是这样。看来this match是失败的,其中 u baseType HConsSym返回<notype> 。相当于:

scala> weakTypeOf[test.gen.Repr].baseType(weakTypeOf[::[_, _]].typeConstructor.typeSymbol)
res12: reflect.runtime.universe.Type = <notype>

这是使用 shapeless 2.3,尽管它在 2.2 中由于不同的原因而失败(似乎 Updater 有一个很大的重构)。

是否可以用 Shapeless 来实现这一点,或者我是否偏离了目标?

最佳答案

这里的主要问题是 LabelledGeneric ( Repr ) 的精炼结果类型丢失。在Model ,唯一已知的关于 ReprRepr <: HList 。隐含的Updater.Aux[gen.Repr, F, gen.Repr]搜索仅名为 _ <: HList 的内容因而未能实现。您必须定义 Model有两个类型参数 abstract class Model[A, L <: HList](implicit gen: LabelledGeneric.Aux[A, L])但这不允许你写 class Test extends Model[Test]并且您必须手动编写标记的泛型类型。

如果您改为移动 gen降至 withId ,你可以让它工作:

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]

如果您关心分辨率性能,可以将值缓存在 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))

将使用 Test.gen 的定义而不是具体化一个新的LabelledGeneric每次。

这适用于 Shapeless 2.2.x 和 2.3.x。

关于scala - 如何使用 LabelledGeneric 一般更新案例类字段?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35738698/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com