Shapeless:找不到更新程序的隐式值
Shapeless: Could not find implicit value for updater
我正在做一个副项目,试图在 Scala 中实现不可变聚合。我的想法是让基本特征 AggregateRoot
具有一些常见的行为。子 classes 将是建模为案例 classes 的实际聚合。现在,有一件事我不喜欢,那就是我不能从基本特征调用 copy
方法,原因有两个:
- 我无法访问基本特征中的
copy
方法
- 我不知道
copy
方法会有多少参数
我对 shapeless 库有一些基本了解,我认为它可能对这种情况有所帮助。我的想法是将标记字段列表传递给特征中的基本方法,该方法将替换它们和 return 案例的新实例 class。
作为朝着这个方向迈出的一步,我正在尝试创建一种方法,该方法将使用 shapeless 开始复制一个字段,但我一直遇到相同的错误,即编译器无法找到更新程序的隐式。
这是我尝试使用的简化代码片段:
import shapeless._
import shapeless.labelled.FieldType
import shapeless.ops.record.Updater
import shapeless.record._
import shapeless.syntax.singleton._
trait AggregateRoot[T <: AggregateRoot[T]] {
self: T =>
def makeCopy[L <: HList, K, V](ft: FieldType[K, V])(
implicit
labeledGen: LabelledGeneric.Aux[T, L],
updater: Updater.Aux[L, FieldType[K, V], L],
witness: Witness.Aux[K]): T = {
val labeledHList = labeledGen.to(this)
val result = labeledHList.updated(witness, ft)
labeledGen.from(result)
}
}
case class User(id: String, age: Int) extends AggregateRoot[User]()
val user1 = User("123", 10)
val ageChange = "age" ->> 22
val user2 = user1.makeCopy(ageChange)
由于我不是经验丰富的用户,我不确定为什么它找不到请求的隐式。 shapeless 的版本是 2.3.3.
据我从这个很好的答案中了解到: - 在一般情况下你不能有 Updater,因为 Shapeless 需要为每个特定字段派生它以防万一 class,这意味着您需要为每个特定字段提供方法,而不是具有通用目的 makeCopy
,例如:
import shapeless._, record._, ops.record._, labelled._, syntax.singleton._, tag._
trait AggregateRoot[T <: AggregateRoot[T]] {
self: T =>
import AggregateRoot._
def withAge[L <: HList, K, V](age: Int)(implicit
gen: LabelledGeneric.Aux[T, L],
upd: Updater.Aux[L, F, L]
): T = {
val ageField = AgeField(age)
gen.from(upd(gen.to(this), ageField))
}
}
object AggregateRoot {
type AgeField = Symbol with Tagged[Witness.`"age"`.T]
val AgeField = field[AgeField]
type F = FieldType[AgeField, Int]
}
import AggregateRoot._
case class User(id: String, age: Int) extends AggregateRoot[User]
object User {
implicit val gen = LabelledGeneric[User]
}
val user1 = User("123", 10)
val ageChange = "age" ->> 22
val user2 = user1.withAge(ageChange)
val test = User("test-id", 20)
println("test.withAge(42) = " + test.withAge(20))
println("test.withAge(12).withAge(42) = " + test.withAge(12).withAge(42))
我正在做一个副项目,试图在 Scala 中实现不可变聚合。我的想法是让基本特征 AggregateRoot
具有一些常见的行为。子 classes 将是建模为案例 classes 的实际聚合。现在,有一件事我不喜欢,那就是我不能从基本特征调用 copy
方法,原因有两个:
- 我无法访问基本特征中的
copy
方法 - 我不知道
copy
方法会有多少参数
我对 shapeless 库有一些基本了解,我认为它可能对这种情况有所帮助。我的想法是将标记字段列表传递给特征中的基本方法,该方法将替换它们和 return 案例的新实例 class。 作为朝着这个方向迈出的一步,我正在尝试创建一种方法,该方法将使用 shapeless 开始复制一个字段,但我一直遇到相同的错误,即编译器无法找到更新程序的隐式。
这是我尝试使用的简化代码片段:
import shapeless._
import shapeless.labelled.FieldType
import shapeless.ops.record.Updater
import shapeless.record._
import shapeless.syntax.singleton._
trait AggregateRoot[T <: AggregateRoot[T]] {
self: T =>
def makeCopy[L <: HList, K, V](ft: FieldType[K, V])(
implicit
labeledGen: LabelledGeneric.Aux[T, L],
updater: Updater.Aux[L, FieldType[K, V], L],
witness: Witness.Aux[K]): T = {
val labeledHList = labeledGen.to(this)
val result = labeledHList.updated(witness, ft)
labeledGen.from(result)
}
}
case class User(id: String, age: Int) extends AggregateRoot[User]()
val user1 = User("123", 10)
val ageChange = "age" ->> 22
val user2 = user1.makeCopy(ageChange)
由于我不是经验丰富的用户,我不确定为什么它找不到请求的隐式。 shapeless 的版本是 2.3.3.
据我从这个很好的答案中了解到:makeCopy
,例如:
import shapeless._, record._, ops.record._, labelled._, syntax.singleton._, tag._
trait AggregateRoot[T <: AggregateRoot[T]] {
self: T =>
import AggregateRoot._
def withAge[L <: HList, K, V](age: Int)(implicit
gen: LabelledGeneric.Aux[T, L],
upd: Updater.Aux[L, F, L]
): T = {
val ageField = AgeField(age)
gen.from(upd(gen.to(this), ageField))
}
}
object AggregateRoot {
type AgeField = Symbol with Tagged[Witness.`"age"`.T]
val AgeField = field[AgeField]
type F = FieldType[AgeField, Int]
}
import AggregateRoot._
case class User(id: String, age: Int) extends AggregateRoot[User]
object User {
implicit val gen = LabelledGeneric[User]
}
val user1 = User("123", 10)
val ageChange = "age" ->> 22
val user2 = user1.withAge(ageChange)
val test = User("test-id", 20)
println("test.withAge(42) = " + test.withAge(20))
println("test.withAge(12).withAge(42) = " + test.withAge(12).withAge(42))