如何为联合类型定义类型类?解决无限递归问题
解决Scala联合类型类型类的无限递归问题
问题原因
你遇到的无限递归是因为在Foo[Value]的扩展方法实现中,模式匹配后的i: Int和s: String在当前上下文里,编译器默认会绑定Foo[Value]这个given实例来解析bar扩展方法,而非对应的Foo[Int]或Foo[String],导致每次调用都会回到Foo[Value]的bar方法,形成递归。
解决方案
方案1:调整类型类结构(推荐)
把bar定义为类型类的核心方法,扩展方法基于类型类方法实现,这样可以直接通过summon获取具体类型的实例并调用方法,彻底避免歧义:
// 重构类型类,将bar作为类型类的核心方法 trait Foo[A]: def bar(a: A): String // 单独定义扩展方法,复用类型类的bar实现 extension [A](a: A)(using f: Foo[A]) def bar: String = f.bar(a) // 保持原有的Int和String实例不变 given Foo[Int] with def bar(a: Int): String = s"Int: $a" given Foo[String] with def bar(a: String): String = s"String: $a" // 联合类型定义 type Value = String | Int // 实现联合类型的Foo实例 given Foo[Value] with def bar(v: Value): String = v match case i: Int => summon[Foo[Int]].bar(i) case s: String => summon[Foo[String]].bar(s)
方案2:显式指定using实例(无需修改原类型类)
如果不想改动原类型类结构,可以在调用bar时显式传入对应类型的given实例,强制编译器使用正确的类型类实现:
// 保持原类型类定义不变 trait Foo[A]: extension (a: A) def bar: String // 原有的Int和String实例 given Foo[Int] with extension (a: Int) def bar: String = s"Int: $a" given Foo[String] with extension (a: String) def bar: String = s"String: $a" // 联合类型定义 type Value = String | Int // 显式指定using实例避免递归 given Foo[Value] with extension (v: Value) def bar: String = v match case i: Int => i.bar(using summon[Foo[Int]]) case s: String => s.bar(using summon[Foo[String]])
验证效果
现在调用Value类型的bar方法会正确分发到对应类型的实现:
val v1: Value = 123 val v2: Value = "hello" println(v1.bar) // 输出 Int: 123 println(v2.bar) // 输出 String: hello
内容的提问来源于stack exchange,提问作者Todd O'Bryan
相关产品推荐
相关产品推荐

