见这个例子:
def hello(a:String, b:String) = println(a + ":" + b)
val m1 = hello("aaa", _ )
m1("bbb")
无法编译,我需要将类型添加到partial方法:
val m1 = hello("aaa", _: String)
为什么scala不知道方法hello
的第二个参数是String
?
答案 0 :(得分:15)
Scala的类型推断是基于流的。方法和函数需要显式参数类型,用于推断其他类型。 参数类型不能从方法或函数体中推断出来。但是,有时参数类型是从外部上下文中获知的,因此不必标记。两个例子,
val f: String => Unit = hello("aaa", _)
val s = Seq(1,2).map(_+1) // Seq[Int].map expects a function of Int argument type
以下是Martin Odersky关于Scala类型推断与ML和Haskell相比的局限性的引用。挑战包括Scala的重载,记录选择和子类型,以及保持简单的需要,
Scala没有Hindley / Milner类型推断的原因是 很难与超载等功能结合使用 ad-hoc变体,而不是类型类),记录选择和子类型。 我不是说不可能 - 有许多扩展 结合这些功能;事实上,我一直对他们中的一些人有所了解 我。我只是说要很好地完成这项工作是非常困难的 练习,其中一个人需要有小型表达,而且好 错误消息。这也不是一个关闭案例 - 很多研究人员都是 努力推动这里的界限(例如在雷米看看 MLF)。但是现在它是更好的类型推理对比的权衡 更好地支持这些功能。你可以做出权衡 方法。我们想要与Java集成的事实倾向于扩展 支持亚型和远离欣德利/米尔纳。
来源:帖子Universal Type Inference is a Bad Thing下的评论。
答案 1 :(得分:4)
简单地说,Scala使用参数类型来搜索适当的方法,而不是方法类型来推断参数的类型。
要执行您想要的操作,必须使用两个参数搜索所有可能的hello
调用,其中第一个String
- 可能包含隐式转换 - 然后,如果找到一个最具体的选项,用它来推断第二个参数的类型。它必须对它已经完成的所有进行,减慢甚至更多已经是一个相当慢的编译速度。并非不可能,但它没有那样做。
答案 2 :(得分:3)
可能是因为此定义存在潜在的歧义,因为hello
可能会过载。
// inside some class context
def hello(a:String, b:String) = println(a + ":" + b)
def hello(a:String, b:Int) = println(a + ":" + b.toString)
val m1 = hello("aaa", _ ) // which one to choose?
考虑到不仅是你能做val m1 = hello("aaa", _)
的人。您的班级可能有val my_hello = (new C).hello("aaa", _)
的用户。然后你通过在原始字符串hello方法中添加一个重载来破坏源代码兼容性,因为突然间它不再清楚应该做什么了。
我不确定这是唯一的原因,但我们当然可以将其视为安全措施。