【问题标题】:Type inference with existential type存在类型的类型推断
【发布时间】:2026-01-22 07:00:01
【问题描述】:

我有一个通用特征SomeTrait 定义如下:

trait SomeTrait[T] {
  def foo(t: T): String
}

和方法barqux 一样:

def bar[T](t: SomeTrait[T]): T
def qux: List[SomeTrait[_]]

我无法控制上述内容。我正在尝试对qux返回的列表进行操作,像这样

qux map { x => x.foo(bar(x))}

但是,编译器抱怨类型不匹配。据我所知,这应该没问题。

我尝试添加一个通用方法(签名[T](SomeTrait[T])String),并调用它来完成工作,但编译器仍然抱怨。我可以像这样绕过它:

qux map { x =>
  val casted = x.asInstanceOf[SomeTrait[T forSome { type T }]] // !!!
  casted.foo(bar(casted))
}

但这更令人困惑,因为x 已经有SomeTrait[_] 类型,而SomeTrait[T forSome { type T }] 的含义相同。我知道的唯一区别是前者是后者的简写,它使编译器创建自己的合成名称。我希望有更好的方法来做到这一点。我见过this question,但我认为它不适用。

【问题讨论】:

  • 后一个例子也不能编译。 bar 预期类型 T,但 qux 基本上返回一个 List[SomeTrait[Any]],这与它的类型 T 参数不匹配。
  • @YuvalItzchakov 如果您尝试def qux: List[SomeTrait[Any]],它会正常工作,所以这不是问题。
  • @YuvalItzchakov 后者编译得很好。
  • @HTNW 你能发布完整的复制品吗?
  • @YuvalItzchakov From the REPL

标签: scala generics


【解决方案1】:

正确的做法是使用类型变量为T命名:

qux map { case x: SomeTrait[t] => x.foo(bar(x)) }

这样编译器就知道bar(x): t,因此它是x.foo 可接受的参数。

或者,或者,您可以将foobar 组合成一个方法(请记住,方法可以是本地的,因此您可以在需要的地方定义它):

def fooOfBar[T](x: SomeTrait[T]) = x.foo(bar(x))
qux map { fooOfBar(_) }

【讨论】:

  • 什么是t 以及x.foo 的可接受参数如何?你能详细说明一下吗?
  • @YuvalItzchakov 这是一个类型变量模式,见scala-lang.org/files/archive/spec/2.11/…。正如第一句所说,它只是让您为模式匹配中的存在参数命名。因为SomeTrait[T].foo需要T参数,那么SomeTrait[Int].foo当然需要IntSomeTrait[t].foo需要t
  • 后者实际上真的很奇怪,因为省略大括号 (qux map fooOfBar) 会导致编译器再次抱怨类型。但它的工作方式很好。为什么会这样?
  • 如果你写qux map fooOfBar,这真的是qux map fooOfBar[T],其中T必须由编译器推断。并且没有T 的值可以工作(如果可以,则qux 的所有元素都必须是一个T)。 qux map { fooOfBar(_) }qux map { x => fooOfBar[T](x) } 的缩写,现在 T 可以依赖于 x