【问题标题】:Simple Type Inference in ScalaScala 中的简单类型推断
【发布时间】:2009-10-07 07:05:43
【问题描述】:

我一直在研究 Scala 中的类型推断,关于为什么在某些情况下必须显式声明表达式/方法返回类型,我想更好地理解一些事情。

显式 return 声明

示例(如果省略了return 关键字,则有效):

def upCase(s: String) = {
  if (s.length == 0)
    return s    // COMPILE ERROR - forces return type of upCase to be declared.
  else
    s.toUpperCase()
}

为什么不声明返回类型就不能使用显式类型的参数作为返回值?这不仅适用于直接参数引用,也适用于任何“类型可推断”的表达式。

方法重载

示例(添加第二个joiner方法时编译失败):

def joiner(ss: List[String], sep: String) = ss.mkString(sep)

def joiner(ss: List[String]) = joiner(strings, " ")   // COMPILE ERROR WHEN ADDED

【问题讨论】:

  • 也许一个例子来说明 Scala 应该做什么以及它做什么会是个好主意!
  • 由于 joiner 方法被重载,您的第二个示例在没有显式返回类型的情况下无法编译 - 尽管同样不完全清楚为什么 Scala 需要此限制
  • 我编辑了这个问题,希望它会得到社区更好的处理。这有点争论,但这里一个有趣的问题。 OP - 如果您对我的编辑不满意,请随时回滚(或要求我回滚)。
  • 再次感谢您的关注。我不想变得“好斗”;你的修改很棒。
  • 我目前对这些怪癖的怀疑是设计师关心的是编写单遍编译器;另外,我认为这里涉及到编译速度。我这么说是因为我想要的功能可以很容易地添加,从而导致(非常)少的开销。实际上,我不知道 'scalac' 是否是单通道)

标签: scala type-inference


【解决方案1】:

最明显的答案是:因为它在规范中说明,请参阅 scala 参考的第 6.20 部分。但是为什么要这样设计确实是一个非常有趣的问题。我怀疑它与编译器无法预测该表达式将是最后一个这一事实有关,因为 return 改变了执行流程。

编辑:

如果 return 不需要明确的返回类型,请考虑以下代码:

def bar() = {   
  if(guard())  
    return "SS"  
  else if(gurard1())  
    return true   
  2  
}

在这种情况下bar应该有那个返回类型?好吧,最常见的超类型有一个选项,但我认为它会让我们在很多情况下返回 Any。好吧,这只是我的想法,可能完全不正确=)

【讨论】:

  • -“我怀疑它与编译器无法预测表达式将是最后一个的事实有关,因为 return 改变了执行流程。” ...不太明白)
  • 但是,我想,你可以去掉“return”子句,它不会改变任何东西。 (我的意思是歧义仍然存在)
  • 肯定声明return明确保证这是最后一个表达式!然后 scala 可以只做所有 return 语句的公共超类型......
  • @Bubba88 不,如果您消除 return 它将更改所有其他表达式(在我的示例中只是 '2' 但假设还有更多)将被执行。 @oxbow_lakes 不。我在编辑中给出的代码中的最后一个表达式是什么?这取决于是否有任何一个守卫功能返回真实。我已经提到了最常见的超类型解决方案 BTW
【解决方案2】:

函数或方法的类型是从其最后一条语句的类型推断出来的。通常,这是一种表达方式。

现在,“return”中断了控制流。可以这么说,这是一个“立即中断”。因此,不能再使用用于推断表达式类型的常规规则。当然,它仍然可以完成,但我猜编译器复杂性的成本被认为是高回报。

以下是流程中断的示例:

def toNumber(s: String) = {
  if (s == null)
    return ""

  if (s matches """\d+""")
    s.toInt
  else
    0
}

通常,第二个if 语句的类型将用于推断整个函数的类型。但是第一个if 上的return 从函数中引入了第二个返回点,所以这条规则不起作用。

【讨论】:

  • 我不能同意,“编译器复杂性的成本被认为是高回报”。我们可以检查所有返回子句和路径,在编译时通向它们;毕竟 - 编译器不需要检查函数体中每个表达式的类型来解析最后一个吗?
  • 非常愚蠢的例子是在一个函数中声明两个“无类型”变量(由数字初始化)并将它们相乘作为最终表达式:编译器必须在告诉结果的类型之前检查这两个变量,因为它可以既可以是整数,也可以是浮点数或长整数等。我实际上并不知道 'scalac'-s 类型推理算法,但有件事告诉我,实际上无法避免递归驱动。
  • 类型推断逐个语句起作用。如果在陈述结束时无法弄清楚某些事情,那么它就会失败,即使进一步的事情会提供足够的信息来决定。现在,类型推断算法可能是 Scala 编译器中最复杂的部分,事实上,它甚至都没有指定,因此扩展它并不是一件容易的事,复杂性的增加会对正确性和可维护性产生影响的编译器。据我所知,这条线是根据松散的成本/收益分析得出的。
  • Daniel,抱歉,您似乎并不完全正确。我们实际上可以(自动)推断出“return”表达式的类型,因为我们需要的只是它前面的表达式类型。再加上“类型推断逐个语句起作用”,我认为这很容易得到——我们不需要比“return”子句进一步计算!)因为我们知道,return 语句始终是最后一个语句执行流程。关于规格,尼古拉说:“最明显的答案是:因为它在规格中说明,请参阅 scala 参考的第 6.20 部分..”
  • return 声明并不总是最后一个。例如,请参阅我的示例。
【解决方案3】:

类型推断在可能的情况下推断方法的返回类型,这或多或少在任何情况下方法不是递归的。

如果您将示例更改为:

def upCase(s: String) = {
 if (s.length == 0)
   s    // note: no return
 else
   s.toUpperCase()
}

我不知道为什么 return 会改变这个。

【讨论】:

    【解决方案4】:

    免责声明 - 此答案针对最初发布的问题

    Scala 的类型推断已经推断出方法/表达式的返回类型:

    scala> def foo(s : String) = s + " Hello"
    foo: (String)java.lang.String
    
    scala> var t = foo("World")
    t: java.lang.String = World Hello
    

    和:

    scala> def bar( s : String) = s.toInt
    bar: (String)Int
    
    scala> var i = bar("3")
    i: Int = 3
    

    和:

    scala> var j = if (System.getProperty("user.name") == "oxbow") 4 else "5".toInt
    j: Int = 5
    

    编辑 - 我没有意识到包含 return 关键字意味着必须显式声明表达式的返回类型:我几乎不再使用 return我自己——但这是一个有趣的问题。对于joiner 示例,由于重载,必须声明返回类型。同样,我不知道原因的详细信息,并且有兴趣了解。我怀疑一个措辞更好的问题主题会从 James Iry、Dan Spiewak 或 Daniel Sobral 等人那里得到答案。

    【讨论】:

    • return 是多余的,可以省略——尽管问 “为什么显式返回需要显式返回类型?” 是个好问题
    【解决方案5】:

    我怀疑方法重载(缺少)推断与递归调用的类似问题有关,因为如果重载的方法不相互调用,它可以完美地工作:

      def joiner1(ss: List[String], sep: String) = ss.mkString(sep)
      def joiner(ss: List[String], sep: String) = ss.mkString(sep)
      def joiner(ss: List[String]) = joiner1(ss, " ")  
    

    有两个重载的 joiner 方法,但类型被正确推断代码编译。

    【讨论】:

    • 我也这么认为。但我想知道方法重载与递归有何关系;我的意思是,重载方法只是两个或多个不同的方法,没有任何逻辑关系。
    • 您是否尝试过在 Scala 邮件列表中提问?你会得到编译器的原因或修复:)
    猜你喜欢
    • 2012-11-29
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多