为什么广义类型约束不能完全影响方法体内的编译器?

问题描述

编译器确实知道类型参数 T方法体内部的 String,因为以下示例中的广义类型约束 T =:= String

scala> def f[A](a: A)(implicit ev: A =:= String) = a.toupperCase
def f[A](a: A)(implicit ev: A =:= String): String

然而,尽管拥有这些知识,但似乎还没有完全应用。例如考虑我们如何不能reverse a String

scala> def f[A <: String](a: A) = a.reverse
def f[A <: String](a: A): String

scala> def f[A](a: A)(implicit ev: A <:< String) = a.reverse
                                                     ^
       error: value reverse is not a member of type parameter A

或者尽管编译器知道 StringA,但我们如何无法将 A 值分配给 String

scala> def f[A](a: String)(implicit ev: A =:= String) = { var x: A = a }
                                                                     ^
       error: type mismatch;
        found   : a.type (with underlying type String)
        required: A

如何解释这种行为,即为什么广义类型约束在方法体内部不完全有效?

解决方法

在收集埋藏在评论中的 Luis 点的传统中,=:=<:< 的语法糖的美丽可能掩盖了它们没有什么特别之处的事实。它们类似于

scala> def f[A](a: A)(implicit ev: A => String) = a.toUpperCase
def f[A](a: A)(implicit ev: A => String): String

scala> def f[A](a: A)(implicit ev: A => String) = ev.apply(a).reverse
def f[A](a: A)(implicit ev: A => String): String

编译器在必要时利用证据作为隐式转换

def f[A](a: A)(implicit ev: A => String): String = 
  ev.apply(a).toUpperCase

def f[A](a: A)(implicit ev: A => String): String = 
  Predef.augmentString(ev.apply(a)).reverse
              |              |
              |          1st conversion
        2nd conversion

reverse 的情况下,我们不能简单地编写 a.reverse,因为在调用 reverse 之前需要进行两次链式转换,而 Scala 不能自动执行此操作,因此我们需要明确说明链中的一步。

A =:= String 视为 A => String,我们看到类型参数 A 在方法主体内保持未特化,因此例如 var x: A = a 无法工作,因为 A 不是绑定到任何特殊类型。