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

Scala中given ... with {}与given ... = new {}为何表现不同?

Scala中given两种实现方式的差异解析

问题场景

以下代码尝试通过DoubleEncoder串联两个Transformer实现Int -> Y的转换,但最后一行编译失败:

// 定义转换特质
trait Transformer[A, B] {
  def transform(a: A): B
}

// 定义带中间类型的双层编码器特质
trait DoubleEncoder[From, To] {
  type Intermediate
  
  def transformer(using Transformer[From, Intermediate], Transformer[Intermediate, To]): Transformer[From, To]
}

// 定义中间类型及对应的Transformer实例
class X(i: Int)
class Y(x: X)

given Transformer[Int, X] = X.apply _
given Transformer[X, Y] = Y.apply _

// 原始的DoubleEncoder实现(编译失败)
given DoubleEncoder[Int, Y] = new DoubleEncoder[Int, Y] {
  type Intermediate = X
  
  override def transformer(using t1: Transformer[Int, X], t2: Transformer[X, Y]): Transformer[Int, Y] =
    new Transformer { def transform(i: Int): Y = t2.transform(t1.transform(i)) }
}

// 通过DoubleEncoder自动生成Transformer的隐式规则
given [P, Q](using de: DoubleEncoder[P, Q])(using Transformer[P, de.Intermediate], Transformer[de.Intermediate, Q]): Transformer[P, Q] =
  de.transformer

// 编译失败:无法找到Transformer[Int, Y]的隐式实例
summon[Transformer[Int, Y]].transform(1)

编译报错核心为:无法为DoubleEncoder[Int, Y]找到匹配的Transformer[Int, de.Intermediate]和Transformer[de.Intermediate, Y]实例,编译器无法推断de.Intermediate的具体类型。

将DoubleEncoder的实现修改为以下写法后,代码即可正常编译运行:

// 修改后的DoubleEncoder实现(编译成功)
given DoubleEncoder[Int, Y] with {
  type Intermediate = X
  
  override def transformer(using t1: Transformer[Int, X], t2: Transformer[X, Y]): Transformer[Int, Y] =
    new Transformer { def transform(i: Int): Y = t2.transform(t1.transform(i)) }
}

核心原因解析

1. 两种写法的类型信息差异

  • 第一种写法(new DoubleEncoder[Int, Y] { ... }):通过匿名类创建实例后,将其向上转型为DoubleEncoder[Int, Y]。这个过程会丢失实例内部的细化类型信息——编译器只能识别它是DoubleEncoder[Int, Y],但无法知晓其Intermediate类型被具体化为X,因为trait DoubleEncoder本身的Intermediate是抽象的。
  • 第二种写法(given ... with { ... }):这是Scala 3专为given设计的特质实现语法,不会进行向上转型。编译器会将该实例视为DoubleEncoder[Int, Y] { type Intermediate = X }的细化类型,完整保留Intermediate的具体类型信息。

2. 编译失败的直接原因

当处理最后一个通用given规则时,需要根据de: DoubleEncoder[P, Q]的Intermediate类型匹配对应的Transformer实例。第一种写法的DoubleEncoder实例丢失了Intermediate = X的信息,编译器无法推断出需要匹配Transformer[Int, X]和Transformer[X, Y],导致隐式解析失败。

补充提问解答:为何参数类型是DoubleEncoder[P, Q]仍有差异

虽然通用given的参数声明为DoubleEncoder[P, Q],但Scala的隐式解析会追踪实例的实际运行时类型(细化类型)。第二种写法的实例带有Intermediate = X的细化类型标记,当它被传入时,编译器能通过类型推导确定de.Intermediate = X,从而匹配到已定义的Transformer实例;而第一种写法的实例因为向上转型丢失了该标记,编译器无法完成类型推断,导致隐式链断裂。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 10:09:53