【问题标题】:Shapeless: Could not find implicit value for updater无形:找不到更新程序的隐含值
【发布时间】:2021-03-03 18:42:10
【问题描述】:

我正在做一个附带项目,试图在 Scala 中实现不可变聚合。我的想法是让基本特征AggregateRoot 具有一些常见的行为。子类将是建模为案例类的实际聚合。目前,我不喜欢一件事,那就是我不能从基本 trait 调用 copy 方法,原因有两个:

  1. 我无法访问基本特征内的 copy 方法
  2. 我不知道copy方法会有多少参数

我对无形库有一些基本知识,我认为它可能对这种情况有所帮助。我的想法是将标记字段列表传递给特征中的基方法,该方法将替换它们并返回案例类的新实例。 作为朝着这个方向迈出的一步,我正在尝试创建一个方法,该方法将使用 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。

【问题讨论】:

  • 我想说的是,添加自己的 withX 方法集而不是 copy 会更容易、更简单和更好

标签: scala generics shapeless case-class


【解决方案1】:

据我从这个很好的答案中了解到:How to generically update a case class field using LabelledGeneric? - 在一般情况下,您不能拥有 Updater,因为 Shapeless 需要为 case 类中的每个特定字段派生它,这意味着而不是具有通用用途 @987654323 @您需要为每个特定字段提供方法,例如:

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))

斯卡蒂:https://scastie.scala-lang.org/kDnL6HTQSEeSqVduW3EOnQ

【讨论】:

  • 谢谢伊万,这很有帮助。首先,我将使用这种方法从基类更新聚合版本。
  • @MilenkoJovanovic 谢谢。如果它有帮助,请投票或/和接受高度欢迎和赞赏。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-29
相关资源
最近更新 更多