You need to enable JavaScript to run this app. Shapeless 2.3.3实现Scala不可变聚合根时找不到Updater隐式值
优惠活动
大模型
产品
解决方案
定价
更多

Shapeless 2.3.3实现Scala不可变聚合根时找不到Updater隐式值

编译报错根因

代码无法解析到Updater隐式,是三处和Shapeless 2.3.3隐式解析规则不匹配的问题,和库版本无关:

  • 硬绑定Updater输出类型导致解析断链
    原代码在Updater签名中,将字段替换后的HList类型硬写为与原记录完全一致的L。但Shapeless生成Updater实例时,替换后的HList是基于输入记录、待更新字段类型独立推导的新类型符号——哪怕结构和原L完全等价,编译器也不会自动将其判定为声明的L类型,直接判定无匹配隐式。
  • 参数逻辑错位加剧匹配失败
    FieldType[K, V]本身在类型层面携带K的标签信息,不需要额外引入Witness.Aux[K]做键传递。原代码调用updated时将witness作为独立键参数传入,走的是“按独立键值更新”的隐式路径,与持有完整FieldType值的更新路径不匹配,进一步阻断隐式查找。
  • 缺少类型等价校验
    Shapeless的LabelledGeneric.from只接受和源类型对应HList严格相等的类型作为输入,哪怕结构等价,类型符号不同也无法编译。原代码未做该校验,编译器在隐式解析阶段就会放弃匹配。
修正后的可运行代码
import shapeless._
import shapeless.labelled.FieldType
import shapeless.ops.record.Updater
import shapeless.syntax.singleton._

trait AggregateRoot[T <: AggregateRoot[T]] {
  self: T =>

  def makeCopy[L <: HList, K, V, UpdatedL <: HList](ft: FieldType[K, V])(
    implicit
    labeledGen: LabelledGeneric.Aux[T, L],
    updater: Updater.Aux[L, FieldType[K, V], UpdatedL],
    // 校验更新后的HList与原HList类型完全等价
    ev: UpdatedL =:= L
  ): T = {
    val labeledHList = labeledGen.to(this)
    // 直接传入完整FieldType,无需单独传递Witness
    val updatedRecord = labeledHList.updated(ft)
    labeledGen.from(updatedRecord)
  }
}

case class User(id: String, age: Int) extends AggregateRoot[User]

// 原有调用逻辑无需修改
val user1 = User("123", 10)
val ageChange = "age" ->> 22
val user2 = user1.makeCopy(ageChange)
适配扩展提示

如果要实现多字段批量更新,只需将单字段FieldType[K, V]参数替换为多字段组成的HList,配合Updater的递归批量实现即可,无需调整特质的通用结构。


内容的提问来源于stack exchange,提问作者Milenko Jovanovic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.31 23:45:09