【问题标题】:Type inference on method return type方法返回类型的类型推断
【发布时间】:2011-01-13 15:07:01
【问题描述】:

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

例如,为什么下面的代码会编译?

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)
}

【问题讨论】:

    标签: scala type-inference


    【解决方案1】:

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

    当您在方法中使用return 时,您会引入另一个语句,该方法可能会从该语句返回。这意味着 Scala 在找到它时无法确定 return 的类型。相反,它必须一直持续到方法结束,然后结合所有出口点来推断它们的类型,然后返回到每个出口点并分配​​它们的类型。

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

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

    【讨论】:

    • 你好丹尼尔。我不明白你的解释。由于 if/else 语句,Scala 已经不得不在函数中组合多个表达式和退出点。 Scala 语言中有大量极其复杂的东西,IMO 大多数 Scala 程序员不太了解或使用这些东西(例如协变/逆变、结构类型等)。这给编译器增加了很多复杂性;所以“使编译器更复杂”似乎是一个薄弱的答案。
    • @UrbanVagabond 你错过了“获得成为领导者”的部分。仅仅因为某事很复杂并不意味着值得为其添加更多复杂性。现在,Scala 不必在 if/else 语句上组合多个表达式和退出点,因为 if/else 是一个表达式,而不是语句。这可能看起来像分裂的头发,但差异是非常真实的。
    【解决方案2】:

    这会增加编译器(和语言)的复杂性。对类似的东西进行类型推断真的很时髦。与任何与类型推断相关的事情一样,当您只有一个表达式时,一切都会更好。分散的 return 语句有效地创建了许多隐式分支,这些分支很难统一。并不是说它特别,只是很粘。例如:

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

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

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

    因此,就像 Scala 中其他几个“不受欢迎”的功能(例如 asInstanceOf 而不是 as),该语言的设计者做出了慎重的选择,让事情不那么令人愉快。这与它引入类型推断的复杂性以及除了最人为的场景之外的所有结果的实际无用性相结合。 scalac 尝试这种推理是没有任何意义的。

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

    【讨论】:

    • @ Daniel ...“不鼓励”Scala 中的功能(例如 asInstanceOf 而不是 as)” .... 我错过了什么吗?我不记得 as 作为 Scala 中的函数(但我对 Scala 还很陌生,所以这可能是我的错误)。
    • @Daniel,我认为更好的建议是避免使用returns(相关:stackoverflow.com/questions/3770989/…
    • Scala 中没有 as 函数。 C# 中有一个 as 运算符,尽管它的功能类似于 Scala 中的 asInstanceOf 方法。很多刚接触该语言的人会问为什么 Scala 的强制转换机制如此冗长,答案很简单,就是不鼓励使用它。
    • IMO“不要使用返回语句”是非常虚假的。在大型函数中,'return' 的存在清楚地表明了退出发生的位置,否则可能完全不明显——嵌套的 if 语句和匹配语句可能会在整个函数中散布大量退出点,没有明确的表明这一点。
    【解决方案3】:

    鉴于此(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
    

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 2021-06-11
        • 2016-05-26
        • 1970-01-01
        • 2020-04-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多