方法返回类型的类型推断

29
为什么Scala在方法中使用显式的return语句时无法推断出返回类型呢?例如,为什么以下代码会编译通过?
object Main {
    def who = 5
    def main(args: Array[String]) = println(who)
}
但是以下内容却不行。
object Main {
    def who = return 5
    def main(args: Array[String]) = println(who)
}
4个回答

31

一个方法的返回类型要么是定义该方法代码块中最后一个语句的类型,要么是在没有代码块的情况下定义它的表达式的类型。

当你在方法中使用return时,你引入了另一个可以从方法中返回的语句。这意味着Scala无法确定该return的类型。相反,它必须继续执行直到方法结束,然后将所有退出点组合起来以推断它们的类型,然后回到每个退出点并分配它们的类型。

这样做会增加编译器的复杂性并减慢编译速度,唯一的好处是在使用return时不必指定返回类型。另一方面,在现有系统中,推断返回类型是从Scala已经使用的有限类型推断中免费获得的。

因此,在编译器复杂性和所获得的收益之间的平衡中,后者被认为不值得前者的代价。


5
嗨,丹尼尔。我不理解你的解释。由于 if/else 语句的存在,Scala已经需要在函数中合并多个表达式和退出点。而且,Scala语言中有很多非常复杂的东西,在我看来,大多数Scala程序员并没有很好地理解或使用(例如协变/逆变、结构类型等)。这给编译器增加了很多复杂性;因此,“使编译器更复杂”似乎是一个弱弱的回答。 - Urban Vagabond
5
@UrbanVagabond,你忽略了“收益不足”的部分。仅仅因为某件事情很复杂,并不意味着值得增加更多的复杂度。现在,Scala不需要在if/else语句上组合多个表达式和退出点,因为if/else是一个表达式,而不是语句。这可能看起来很微小,但是差异非常显著。 - Daniel C. Sobral

11

这会增加编译器(和语言)的复杂性。对于像这样的东西进行类型推断,只是非常奇怪。与任何与类型推断有关的事情一样,当您有一个单独的表达式时,所有内容都可以更好地工作。分散的返回语句有效地创建了许多隐含的分支,这变得非常棘手难以统一。并不是说这特别困难,只是很棘手。例如:

def foo(xs: List[Int]) = xs map { i => return i; i }

在这里,编译器究竟推断了什么?如果编译器在显式的返回语句中进行推断,那么它需要是Any类型。实际上,许多带有显式返回语句的方法最终都会返回Any类型,即使你不使用非本地返回也是如此。就像我说的,难以捉摸。

除此之外,这不是应该鼓励的语言特性。除非只有一个显式返回且在函数末尾,否则显式返回并不能提高代码的清晰度。原因很容易理解,如果将代码路径视为有向图,则分散的返回语句将产生大量的隐式分支,导致图中出现奇怪的叶子节点以及主体中的许多额外路径。这太棘手了。如果你的分支都是显式的(模式匹配或者if表达式),控制流程会更容易看到,而且如果你不依赖于具有副作用的return语句来生成值,你的代码也将更加功能化。

所以,就像Scala中的其他“不建议”的特性一样(例如asInstanceOf而不是as),语言设计者故意选择使事情变得更加不愉快。再加上它引入类型推断的复杂性以及结果在除最为假想的场景外实际上没有什么用处。这样的推断对于scalac来说毫无意义。

故事的寓意是:学会不要分散你的返回语句!这是在任何语言中都很好的建议,不仅仅是Scala。


@ Daniel...Scala中的“不鼓励”功能(例如asInstanceOf而不是as)...我错过了什么吗?我不记得在Scala中有as作为函数(但我对Scala还比较新,所以可能是我的错误)。 - Jus12
@ Daniel,我认为更好的建议是避免使用 return(相关内容:https://dev59.com/Gm865IYBdhLWcg3whfBf)。 - Jus12
1
在Scala中没有as函数。但是在C#中有一个as运算符,其功能类似于Scala中的asInstanceOf方法。很多刚接触这门语言的人都会质疑为什么Scala的类型转换机制如此冗长,答案很简单,那就是为了阻止过度使用它。 - Daniel Spiewak
3
在我看来,“不要使用return语句”这种说法相当荒谬。在一个大函数中,存在“return”语句可以清楚地表明退出点的位置,否则可能完全不明显——嵌套的if语句和匹配语句可能会在整个函数中散布许多退出点,而没有明确的指示。 - Urban Vagabond

1

考虑到这个(2.8.Beta1):

object Main {
  def who = return 5
  def main(args: Array[String]) = println(who)
}
<console>:5: error: method who has return statement; needs result type
         def who = return 5

...它似乎不是无意的。


-2

我不确定为什么。也许只是为了阻止使用return语句。:)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接