Scala中的简单类型推断

Scala中的简单类型推断,scala,type-inference,Scala,Type Inference,我一直在研究Scala中的类型推断,关于为什么在少数情况下必须显式声明表达式/方法返回类型,有几件事我希望能够更好地理解 显式返回声明 示例(如果输入了return关键字,则有效): 为什么我不能在不声明返回类型的情况下使用显式类型化参数作为返回值?这不仅适用于直接参数引用,也适用于任何“类型可推断”表达式 方法重载 示例(添加第二个joiner方法时无法编译): 免责声明-此答案直接针对最初发布的问题 Scala的类型推断已经推断出方法/表达式的返回类型: scala> def foo(

我一直在研究Scala中的类型推断,关于为什么在少数情况下必须显式声明表达式/方法返回类型,有几件事我希望能够更好地理解

显式
返回
声明

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

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

方法重载

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


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

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

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

scala> var t = foo("World")
t: java.lang.String = World Hello
以及:

以及:


EDIT-我没有意识到包含
return
关键字意味着必须显式声明表达式的返回类型:我自己几乎已经停止使用
return
,但这是一个有趣的问题。对于
joiner
示例,由于重载,必须声明返回类型。再说一次,我不知道原因的细节,我很想知道。我怀疑一个措辞更好的问题主题会从詹姆斯·艾里、丹·斯皮瓦克或丹尼尔·索布拉尔这样的人那里得到答案

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

如果将示例更改为:

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

我不知道为什么返回会改变这一点。

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

编辑:

考虑返回是否不需要显式返回类型,代码如下:

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

在这种情况下,返回类型应该是bar吗?大多数常见的超类型都有选择,但我认为在很多情况下,它会让我们返回任何超类型。这只是我的想法,可能是完全错误的=)

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

现在,“
return
”中断了控制流。可以说,这是一种“即时中断”。因此,无法再使用用于推断表达式类型的常规规则。当然,这仍然是可以做到的,但我猜编译器复杂性的成本对于回报来说是很高的

下面是一个流程如何中断的示例:

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

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

通常,第二个
if
语句的类型将用于推断整个函数的类型。但是第一个
上的
返回
if
引入了函数的第二个返回点,因此该规则不起作用。

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

  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应该做什么以及它做什么是一个好主意!您的第二个示例在没有显式返回类型的情况下无法编译,因为
joiner
方法被重载-尽管仍然不完全清楚Scala为什么需要这个限制。我编辑了这个问题,希望它能得到社区更好的处理。这有点争论,但这里有一个有趣的问题。OP-如果您对我的编辑不满意,请随时回滚(或要求我回滚)。再次感谢您的关注。我不想变得“咄咄逼人”;您的修订非常好。我目前对这些怪癖的怀疑是,设计师关心的是编写单通道编译器;此外,我认为,这里涉及到编译速度。我这么说是因为我想要的特性可以很容易地添加,从而产生(非常)小的开销。事实上,我不知道‘scalac’是否是单次返回(single pass)返回是多余的,并且可以使用——尽管问“为什么显式返回需要显式返回类型?”是个好问题——“我怀疑这与编译器无法预测表达式将是最后一个表达式这一事实有关,因为返回更改了执行流。”…不太明白)然而,我想,你可以删除“返回”条款,它不会改变什么。(我的意思是歧义仍然存在)声明
return
肯定会明确保证这是最后一个表达式!然后scala可以对所有
return
语句执行一个公共超类型…@Bubba88否,如果消除return,它将更改所有其他表达式(在我的示例中,仅为“2”,但假设还有更多表达式)的执行@oxbow_湖没有。我在编辑中给出的代码中的最后一个表达式是什么?这取决于这两种功能是否正确。我一直提到最常见的超类型解决方案BTWI不能同意,“编译器复杂度的成本被认为是返回的高成本。”我们可以检查所有返回子句和路径,在编译时导致它们;毕竟-编译器不需要检查函数体中每个表达式的类型来解析最终的表达式吗?非常愚蠢的例子是在函数中声明两个“无类型”变量(由数字初始化),并将它们相乘为最终表达式:编译器必须先检查两个变量,然后再告诉结果的类型,因为它既可以是整数,也可以是浮点数,也可以是长数等等。我不知道scalac的类型推断算法,但有东西告诉我,递归驱动
def upCase(s: String) = {
 if (s.length == 0)
   s    // note: no return
 else
   s.toUpperCase()
}
def bar() = {   
  if(guard())  
    return "SS"  
  else if(gurard1())  
    return true   
  2  
}
def toNumber(s: String) = {
  if (s == null)
    return ""

  if (s matches """\d+""")
    s.toInt
  else
    0
}
  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, " ")