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
相关产品推荐
相关产品推荐

