Scala宏实现中类型堆叠场景的单一基类复用与类型推断修正方案问询
解决Scala宏中单一基类的类型推断问题
你遇到的问题本质是Scala路径依赖类型的解析逻辑导致的——当基类Person_base的字段(name/age)引用Push类型时,默认会绑定到最外层可见的Push定义(也就是PersonB.Push),而不是当前实例所属子类的Push。要解决这个问题,咱们可以用**F界多态(F-bounded polymorphism)**让基类的类型成员和具体子类关联起来,确保字段类型能正确指向子类的Push实现,同时保持单一基类的复用性。
核心思路
- 给基类添加一个类型参数
Self,通过F界约束Self <: Person_base[Self]确保这个参数就是继承基类的具体子类 - 基类的字段类型不再直接用
Push[T],而是引用Self#Push[T],这样就能绑定到子类的Push定义 - 每个子类实现时,把
Self指定为自身类型,让基类字段的类型正确指向自己的Push
完整可运行代码
object SetupFixed { import scala.reflect.macros.blackbox import scala.language.experimental.macros trait Init trait Name { val name: String = "" } trait Age { val age: Int = 0 } // 带F界约束的单一基类,复用name/age字段定义 trait Person_base[Self <: Person_base[Self]] { val name: Self#Push[Name] = ??? val age : Self#Push[Age] = ??? } // 顶层对象,指定Self为自身类型PersonB.type object PersonB extends Person_base[PersonB.type] { type Push[T] = Init with Person1[Init with T] } // 中间层Trait,指定Self为自身类型Person1[Stack] trait Person1[Stack] extends Person_base[Person1[Stack]] { type Push[T] = Person2[Stack with T] } trait Person2[Stack] def impl[T](c: blackbox.Context)(person: c.Tree): c.Tree = { import c.universe._ q""" new Init with Name with Age { override val name = "Ben" override val age = 42 } """ } def macroB[T](person: Person2[T]): T = macro impl[T] } import SetupFixed._ // 现在类型正确推断为Init with Name with Age val personB = macroB(PersonB.name.age) personB.name == "Ben" // 结果为true personB.age == 42 // 结果为true
为什么这个方案有效?
- 当你调用
PersonB.name时,返回的类型是PersonB#Push[Name],也就是Init with Person1[Init with Name],这正是我们期望的类型 - 接着调用
.age时,当前实例的类型是Person1[Init with Name],它的Push[Age]定义是Person2[Init with Name with Age],所以最终传递给宏的参数类型是Person2[Init with Name with Age],宏就能正确提取出堆叠的类型Init with Name with Age - 整个方案只需要维护一个基类
Person_base,完全避免了原来多基类的代码冗余问题
内容的提问来源于stack exchange,提问作者Marc Grue
相关产品推荐
相关产品推荐

