通过方法类型参数分配给类型成员的值打破了类型等价

Value assigned to type member via method type parameter breaks type equivalence

为什么下面的类型等价成立

trait Foo { type T }
val fa = new Foo { type T = Int }

implicitly[fa.T =:= Int] // OK

但是当通过方法参数 A 分配类型成员 T 时,类型等价性不成立

def makeFoo[A]: Foo = new Foo { type T = A }
val fb = makeFoo[Int]

implicitly[fb.T =:= Int] // ERROR

凭直觉我会期望如果 T = AA = Int 那么 T = Int?

这是因为 makeFoo 的 return 类型只是 Foo 而不是 Foo { type T = A }。如果您没有显式声明它或者如果您优化它,错误就会消失。

def makeFoo[A] = new Foo { type T = A }
val fb = makeFoo[Int]

implicitly[fb.T =:= Int] // No more error

or

def makeFoo2[A]: Foo { type T = A } = new Foo { type T = A }
val fc: Foo { type T = Int } = makeFoo[Int]

implicitly[fc.T =:= Int] // No more error

Scastie:

<script src="https://scastie.scala-lang.org/knIfPcXqSQKXXCXJ2teHkg.js"></script>

我将添加这种可能性,不指定精确的 return 类型,而是指定其超类型(上限)称为 whiteboxity。

在 Scala 2 中,这可以通过 whitebox macros

实现
import scala.language.experimental.macros
import scala.reflect.macros.whitebox

def makeFoo[A]: Foo = macro impl[A]
def impl[A: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
  import c.universe._
  q"new Foo { type T = ${weakTypeOf[A]} }"
}

val fb = makeFoo[Int]
implicitly[fb.T =:= Int] //compiles

在 Dotty 中,这可以通过关键字 transparent (currently)

来实现
transparent inline def makeFoo[A]: Foo = new Foo { type T = A }
val fb = makeFoo[Int]
summon[fb.T =:= Int] //compiles

以前的语法是

inline def makeFoo[A] <: Foo = new Foo { type T = A }