在方法返回类型上键入推断

mis*_*tor 28 scala type-inference

当方法中使用显式return语句时,为什么Scala无法推断方法的返回类型?

例如,为什么以下代码编译?

object Main {
    def who = 5
    def main(args: Array[String]) = println(who)
}
Run Code Online (Sandbox Code Playgroud)

但以下情况并非如此.

object Main {
    def who = return 5
    def main(args: Array[String]) = println(who)
}
Run Code Online (Sandbox Code Playgroud)

Dan*_*ral 30

方法的返回类型是块中定义它的最后一个语句的类型,或者是在没有块的情况下定义它的表达式的类型.

return方法内部使用时,引入另一个方法可能返回的语句.这意味着Scala无法确定return找到它的类型.相反,它必须继续直到方法结束,然后组合所有出口点以推断它们的类型,然后返回到每个退出点并分配它们的类型.

这样做会增加编译器的复杂性并减慢它的速度,因为在使用时不必指定返回类型return.另一方面,在本系统中,推断返回类型来自Scala已经使用的有限类型推断.

因此,最终,在编译器复杂性与所获得的收益之间取得平衡,后者被认为不值得前者.

  • 嗨丹尼尔.我没有得到你的解释.由于if/else语句,Scala必须在函数中组合多个表达式和出口点.Scala语言中有大量令人生畏的复杂内容,IMO大多数Scala程序员都不太了解或使用(例如协方差/逆变,结构类型等).这给编译器增加了很多复杂性; 因此"使编译器更复杂"似乎是一个微弱的答案. (5认同)
  • @UrbanVagabond你错过了"收益成为头"的一部分.仅仅因为某些事情很复杂并不意味着值得增加它的复杂性.现在,Scala不必在if/else语句中组合多个表达式和退出点,因为if/else是表达式而不是语句.这可能看起来像分裂头发,但差异是非常真实的. (4认同)

Dan*_*wak 11

这会增加编译器(和语言)的复杂性.在类似的东西上进行类型推断真的很时髦.与任何类型的推理相关,当你有一个表达式时,一切都会更好.分散的返回语句有效地创建了许多隐式分支,这些分支在统一时变得非常棘手.这不是特别,只是粘性.例如:

def foo(xs: List[Int]) = xs map { i => return i; i }
Run Code Online (Sandbox Code Playgroud)

我问你,编译器在这里推断出什么?如果编译器使用显式返回语句进行推理,则需要进行推理Any.事实上,很多带有显式返回语句的方法最终都会返回Any,即使你没有偷偷摸摸非本地回报.就像我说的那样,粘.

最重要的是,这不是一个应该鼓励的语言功能.显式返回不会提高代码清晰度,除非只有一个显式返回,并且在函数末尾.如果您将代码路径视为有向图,则很容易看出原因.正如我之前所说的,分散的返回会产生大量的隐式分支,这会在图形上产生奇怪的叶子,以及主体中的许多额外路径.这很时髦.如果您的分支都是显式的(模式匹配或if表达式),则更容易看到控制流,如果您不依赖副作用return语句来生成值,则代码将更加实用.

因此,像Scala中的其他几个"沮丧"特征(例如asInstanceOf而不是as),该语言的设计者做出了慎重的选择,使事情变得不那么令人愉快.这加上它引入类型推断的复杂性以及除了最人为的场景之外的所有结果的实际无用性.scalac尝试这种推理只是没有任何意义.

故事的道德:学会不分散你的回报!这是任何语言的好建议,而不仅仅是Scala.

  • IMO"不使用退货声明"是相当虚伪的.在一个大函数中,'return'的存在使得它清楚地显示了退出的位置,否则可能是完全不明显的 - 嵌套if-statements和match-statements可能在整个函数中分散大量的退出点而没有明确的这表明. (3认同)