为什么Scala不会推断特征类型参数?

问题描述:

trait foo[F] { 
    def test: F 
} 

class ah extends foo[(Int,Int) => Int] { 
    def test = (i: Int,j: Int) => i+j 
} 

所以问题是,为什么斯卡拉知道如此聪明的类型不能只是从test的类型推断出(Int,Int) => Int类型,而是问我而不是指定它?或者它仍然有可能?或者,也许这是由我想不到的一些想法支持的。为什么Scala不会推断特征类型参数?

你的问题基本上是“为什么斯卡拉只有本地类型推断”或等价地“为什么斯卡拉不具有非本地类型推断”。答案是:因为设计师不想。

这有几个原因。其中一个原因是对局部类型推断最合理的选择是全局类型推断,但在Scala中这是不可能的:Scala具有单独的编译和模块化类型检查,因此编译器无法全面了解整个程序。实际上,Scala具有动态代码加载,这意味着在编译时整个代码甚至不需要存在!全球类型推断在Scala中根本不可能。我们所能做的最好的是“整体编译单元类型推断”,但这也是不受欢迎的:这意味着您是否需要类型注释取决于您是以多个单元还是仅以一个单元编译代码。

另一个重要的原因是模块边界处的类型注释用作双重检查,有点像类型的复式簿记。请注意,即使在语言像Haskell这样的全局类型推断中,强烈建议在模块边界和公共接口上放置类型注释。

第三个原因是,全球类型推断有时会导致出现错误消息时,而不是类型检查的类型注释失败,类型inferencer快乐地推断越来越无意义的类型一班班,直到它最后放弃在远离实际错误(这可能只是一个简单的错字)的位置,并且类型错误仅与错误网站的原始类型相切。例如,这有时可能发生在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 
+0

因此,Hindley-Milner能够从'test'推断'foo'的类型吗? – slouc

+0

斯卡拉有子类型,H-M不支持。 (与许多函数式语言不同,Scala的类型系统与HM很不相称)。但是在假设的语言中,使用全局类型推断并且没有子类型和运算符重载,是的,应该可以用零注释来推断所有类型:推断“i ''和'j'和匿名函数的返回类型是来自'+'运算符的整数(记住:没有运算符重载),推断'test'的类型是匿名函数的函数类型,推断'F'成为'test'的返回类型。我认为。 –

+0

是的,我同意推断'test',我只是想知道它是否可以使用这些信息来推断'foo'。 TBH我甚至不知道在H-M中是否支持/允许使用标准类类结构(没有子类型部分;只是一个简单的类型参数化类,它有一个返回该类型的方法)。 – slouc

对于你特定示例中,推断类型参数基于继承可以是不明确的:

trait A 
trait B extends A 

trait Foo[T] { 
    val x: T 
} 

trait Bar extends Foo[?] { 
    val x: B 
} 

可以在?中输入的类型可以是AB。这是Scala不会推断可能不明确的类型的一般示例。

现在,你是正确的观察,有

class Foo[T](x: T) 

trait Foo[T] { x: T } 

我已经看到了一些工作纳入可能概括的相似性(但我似乎无法之间的相似性现在找到它)。理论上,这可以允许基于成员的类型参数的类型推断。但我不知道它是否会达到这一点。

+0

难道那是在Dotty?我的意思是,Dotty没有泛型,他们解析为抽象类型成员。即特质Foo [T]; class IntFoo extends Foo [Int]'实际上是'trait Foo {type T};类IntFoo在Dotty中扩展Foo {override type T = Int}'。 –

+0

通过这个参数,它也不应该推断'def x = new B'的返回类型:它的类型也可以是'A'或'B'(或者'Object'或者'Any')。 –

+0

@AlexeyRomanov你是对的。不同之处在于特征可以随后扩展,所以类型参数既是上限又是下限。这就是为什么推断最合适的最窄类型并不安全。 – Owen