trait foo[F] {
def test: F
}
class ah extends foo[(Int,Int) => Int] {
def test = (i: Int,j: Int) => i+j
}
所以问题是,为什么Scala知道如此聪明的类型不能只从(Int,Int) => Int
的类型中推断类型test
而是要求我指定它?或者仍有可能吗?或许这可以得到一些我没有想到的想法的支持。
答案 0 :(得分:7)
您的问题基本上是“为什么Scala只有本地类型推断”或等效“为什么Scala没有非本地类型推断”。答案是:因为设计师不想这样做。
这有几个原因。一个原因是本地类型推断的最合理的替代方法是全局类型推断,但在Scala中这是不可能的:Scala具有单独的编译和模块类型检查,因此编译器只有整个全局视图的时间点。程序。实际上,Scala具有动态代码加载功能,这意味着在编译时整个代码甚至不需要存在!在Scala中根本不可能进行全局类型推断。我们能做的最好的事情是“整个编译单元类型推断”,但这也是不可取的:它意味着你是否需要类型注释取决于你是否在多个单元中编译代码或只编译一个。
另一个重要原因是模块边界处的类型注释用作复核,类似于类型的复式簿记。请注意,即使在带有全局类型推断的语言(如Haskell)中,强烈建议在模块边界和公共接口上放置类型注释。
第三个原因是全局类型推断有时会导致混淆的错误消息,当类型检查器在类型注释中失败时,类型推断器会快速地推断出越来越不合理的类型,直到它最终 放弃远离实际错误的位置(可能只是一个简单的错字),其类型错误只与错误站点的原始类型相切。例如,这有时会发生在Haskell中。 Scala设计人员非常重视有用的错误消息,以至于他们愿意牺牲语言功能,除非他们能够弄清楚如何使用良好的错误消息来实现它们。 (请注意,即使使用Scala非常有限的类型推断,您也可以获得“预期Foo
得到Product with Serializable
”这样的“有用”消息。)
但是,在此示例中,Scala的本地类型推断可以执行的操作是在另一个方向上工作,并从返回类型test
推断出匿名函数的参数类型:
trait foo[F] {
def test: F
}
class ah extends foo[(Int, Int) ⇒ Int] {
def test = (i, j) ⇒ i + j
}
(new ah) test(2, 3) //=> 5
答案 1 :(得分:1)
对于您的特定示例,基于继承推断类型参数可能不明确:
trait A
trait B extends A
trait Foo[T] {
val x: T
}
trait Bar extends Foo[?] {
val x: B
}
?
中可以包含的类型可以是A
或B
。这是Scala不会推断可能含糊不清的类型的一般示例。
现在,您正确地发现
之间存在相似性class Foo[T](x: T)
和
trait Foo[T] { x: T }
我看到一些人的工作可能会推广相似性(但我现在似乎无法找到它)。从理论上讲,这可以允许基于成员对类型参数进行类型推断。但我不知道它是否会达到这一点。