Scala:为什么函数前面需要类型变量



从处理99个Scala谜题的第一个问题开始,我定义了自己的last版本,如下所示:

def last[A](xs: List[A]): A = xs match {
  case x :: Nil => x
  case x :: xs => last(xs)
}

我的问题是:为什么last后面必须直接跟类型变量,就像last[A]一样?如果我这样写函数,为什么编译器不能做正确的事情:

def last(xs: List[A]): A
    .....

(使[A]离开last[A]的末端?)

如果编译器能够弄清楚,那么以这种方式设计语言的理由是什么?

A出现3次:

  1. last[A]

  2. List[A]

  3. : A(参数列表后)

第二个需要指定List包含类型为A的对象。第三个需要指定函数返回类型为A的对象。

第一个是实际声明A的地方,所以它可以在其他两个地方使用。

您需要写入last[A],因为A不存在。由于它不存在,通过在函数名称后面声明它,您实际上有机会定义该类型的一些期望或约束。

例如:last[A <: Int]以强制执行A必须是Int 的子类型的事实

一旦声明了它,就可以使用它来定义参数的类型和返回类型。

我从@Lee的评论中得到了一个见解:

编译器怎么知道List[A]中的A没有引用实际类型叫做A?

为了向自己证明这是有意义的,我尝试用实际类型String的名称替换类型变量A,然后传递函数a List[Int],看到当last被声明为类似于def last[String](xs: List[String]): String时,我能够传递last a List[Int]:

scala> def last[String](xs: List[String]): String = xs match {
     | case x :: Nil => x
     | case x :: xs => last(xs)
     | }
last: [String](xs: List[String])String
scala> last(List(1,2,3,4))
res7: Int = 4

因此,证明标识符String的行为确实像类型变量,并且不引用具体类型String

如果编译器只是假设任何不在范围内的标识符都是类型变量,那么调试也会变得更加困难。因此,必须在函数定义的开头声明它是有意义的。

最新更新