You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Scala 3联合类型的Typeclass隐式实例正确解析?

问题描述

定义如下类型类及基础实例:

trait ExampleTypeClass[A]
  // 省略若干方法

object ExampleTypeClass:
  given ExampleTypeClass[Int] = new ExampleTypeClass{/* ... */}
  given ExampleTypeClass[Boolean] = new ExampleTypeClass{/* ... */}

已知若存在A和B的类型类实例,可实现A | B类型的实例(A | B与B | A类型等价,逻辑与顺序无关)。尝试实现联合类型的实例:

given unionExampleTypeClass[A, B](
  using a: ExampleTypeClass[A],
  b: ExampleTypeClass[B]
): ExampleTypeClass[A | B] = 
  new ExampleTypeClass[A | B]{
    // 依赖a和b的逻辑(与顺序无关)
  }

显式调用可正常编译:

val intOrBoolTC1: ExampleTypeClass[Int | Boolean] = 
  unionExampleTypeClass[Int, Boolean]

val intOrBoolTC2: ExampleTypeClass[Int | Boolean] = 
  unionExampleTypeClass[Boolean, Int]

但隐式解析失败,报错歧义:

val intOrBoolTC3 = summon[ExampleTypeClass[Int | Boolean]]
// 报错信息:
//[error] Ambiguous given instances: both given instance given_ExampleTypeClass_Int in object ExampleTypeClass and given instance given_ExampleTypeClass_Boolean in object ExampleTypeClass match type ExampleTypeClass[A] of parameter x of method summon in object Predef
// [error] val intOrBoolTC3 = summon[ExampleTypeClass[Int | Boolean]]
// [error]                                                           ^    

核心问题:unionExampleTypeClass[Int, Boolean]与unionExampleTypeClass[Boolean, Int]均生成ExampleTypeClass[Int | Boolean]实例,导致隐式解析歧义。


解决方案

方法一:约束类型顺序避免重复候选

通过编译时约束强制A和B的类型名称按字典序排序,确保同一联合类型仅能匹配一个given实例:

import scala.compiletime.*

// 辅助方法:获取编译时类型的字符串名称
inline def typeName[T]: String = constValue[Type[T]]

// 约束:A的类型名称字典序小于等于B
type ABeforeB[A, B] = (typeName[A] <:< typeName[B]) =:= true

trait ExampleTypeClass[A]
  // 省略方法

object ExampleTypeClass:
  given ExampleTypeClass[Int] = new ExampleTypeClass[Int]{/* ... */}
  given ExampleTypeClass[Boolean] = new ExampleTypeClass[Boolean]{/* ... */}

  // 添加ABeforeB约束,确保A和B顺序唯一
  given unionExampleTypeClass[A, B](
    using a: ExampleTypeClass[A],
    b: ExampleTypeClass[B],
    ev: ABeforeB[A, B]
  ): ExampleTypeClass[A | B] = 
    new ExampleTypeClass[A | B]{
      // 依赖a和b的逻辑
    }

此时,summon[ExampleTypeClass[Int | Boolean]]仅会匹配符合字典序约束的given实例,不会产生歧义。

方法二:利用Sum Mirror自动推导联合类型实例

联合类型本质是Scala 3的Sum类型,可通过Mirror.SumOf自动生成实例,彻底避免顺序问题,同时支持多元素联合类型:

import scala.deriving.Mirror
import scala.compiletime.*

trait ExampleTypeClass[A]:
  // 示例方法,根据实际需求调整
  def handle(x: A): String

object ExampleTypeClass:
  // 基础实例
  given ExampleTypeClass[Int] with
    override def handle(x: Int): String = s"Int: $x"

  given ExampleTypeClass[Boolean] with
    override def handle(x: Boolean): String = s"Boolean: $x"

  // 为任意Sum类型(包括联合类型)自动生成实例
  inline given [T](using m: Mirror.SumOf[T]): ExampleTypeClass[T] =
    new ExampleTypeClass[T]:
      override def handle(x: T): String =
        // 按Sum类型的元素顺序匹配处理
        inline m.ordinal(x) match
          case idx => 
            val elemType = m.MirroredElemTypes[idx]
            summonInline[ExampleTypeClass[elemType]]
              .handle(x.asInstanceOf[elemType])

这种方法扩展性更强,无需手动处理类型顺序,可自动适配任意数量元素的联合类型。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 04:42:06