【问题标题】:What's the type of `=> String` in scala?scala中`=> String`的类型是什么?
【发布时间】:2014-08-20 15:50:12
【问题描述】:

在 scala 中,有一些按名称调用的参数:

def hello(who: => String) = println("hello, " + who)

参数who的类型是什么?

它将scala REPL上的函数显示为:

hello: (who: => String)Unit

类型还是=> String吗?它有什么名字吗?或者一些描述类型的文档?

answer 提出的其他问题

问题 1

(阅读§3.3.1(MethodTypes)的规范时)

方法类型是方法的类型,比如说我定义了一个方法hello

def hello: String = "abc"

它的类型可以写成:=> String,对吧?虽然你可以看到 REPL 的响应是:

scala> def hello:String = "abc"
hello: String

如果我定义一个有参数的方法:

def goodname(name: String): String = name + "!"

方法的类型是什么?它应该类似于String => String,但不是。因为是方法类型,而String => String是函数类型。

问题 2

(阅读§3.3.1(MethodTypes)的规范时)

我可以这样理解:

def goodname(name: String): String = name + "!"
def print(f: String => String) = println(f("abc"))
print(goodname)

当我调用print(goodname)时,goodname的类型会转换为函数类型String => String,对吧?

但是对于无参数方法:

def hello: String = "abc"

可以转换什么函数类型?我试过了:

def print(f: () => String) = println(f())

但这不能编译:

print(hello)

错误是:

错误:类型不匹配; 发现:字符串 必需:() => 字符串

你能给我一个有效的例子吗?

问题 3

(在阅读 §6.26.2 (MethodConversions) 的规范时)

这种求值转换仅在类型未应用于参数时发生。所以,对于代码:

def myname:String = "abc"
def print(name: => String) = println(name)
print(myname)

我的问题是,当我打电话给print(myname) 时,是否发生了转换(我的意思是Evaluation conversion)?我猜,因为myname的类型只是=> String,所以可以直接传给print

如果print 方法发生了变化:

def myname:String = "abc"
def print(name: String) = println(name)
print(myname)

这里Evaluation conversion肯定发生了,对吧?(从=> StringString

【问题讨论】:

  • 类型好像不是()=>String,因为hello(() => "Freewind")无法编译
  • 在您的示例中,myname 的类型是什么?
  • @SeanVieira,修复和改进
  • RE:问题 1 - 方法不是值(第 3.3 节),因此它们本身没有类型。通过将对它们的调用包装在FunctionN 中,它们被转换 为值,其中apply 方法调用底层方法。因此,当您将goodname 传递给print 时,您将Function1[String, String] 的一个实例传递给print,它调用 goodnameprint(new Function1[String, String] { def apply(v: String) = goodname(v) })
  • RE:问题 2 - 无参数方法将转换为 Function0[T]。将hello 传递给print 时遇到问题的原因是编译器调用 hello(因为此类方法“在每次引用无参数方法名称时都会重新评估 ”)。您必须明确告诉编译器使用_ 推迟评估; print(hello _) 而不是 print(hello)

标签: scala callbyname


【解决方案1】:

引用§4.6.1 of the spec:

那么这种参数的类型就是无参数方法类型=> T

因此,按名称调用参数的类型(大约)是() => T(或Function0[T],如果您愿意)。如果你:javap 一个方法接受一个按名称调用的参数,你会看到编译后的代码接受一个scala.Function0<java.lang.Object> 类型的参数。

一个近似的例子

翻译:

def callByName[T](f: => T) = f

callByName { /* magic */
    1 + 1
/* ends here */ }

有效:

def callByName[T](f: Function0[T]) = f.apply()

callByName(new Function0[Int] {
  def apply() = { /* magic */
    1 + 1
  /* ends here */ }
})

对近似值的怀疑

您可能很想尝试将() => T 传递给您的方法。试试callByName(() => 12);为什么它不编译? (提示,考虑调用站点的扩展)。 (悬停在以下空白处查看答案):

callByName(() => 12) 无法编译的原因是因为扩展被视为: callByName(new Function0[() => Int] { 定义应用()=()=> 12 }) 也就是说,不是传入一个返回IntFunction0,而是传入一个返回Function0Function0,它返回一个Int

=> T 实际上是什么

=> T 实际上是一个方法类型,而不是一个对象。因此,之前的所有内容都是编译器所做工作的近似值,并且可以随时更改。引用§3.3:

下面解释的类型并不表示值的集合,它们也没有明确地出现在程序中。它们在本报告中作为已定义标识符的内部类型进行介绍。

那么什么是方法类型?引用§3.3.1 (MethodTypes):

一种特殊情况是没有任何参数的方法类型。它们写在这里=> T。每次引用无参数方法名称时重新计算的无参数方法名称表达式。

方法类型不作为值类型存在。如果将方法名用作值,则其类型会隐式转换为相应的函数类型(§6.26)。

§6.26.3 (MethodConversions) 表示:

以下四种隐式转换可以应用于未应用于某些参数列表的方法。

求值。=> T 类型的无参数方法m 总是通过计算m 绑定到的表达式转换为T 类型。

所以=> T 类型的正确翻译总是:

def random$name$here: T

示例

这是一个可以玩的示例类:

class TestParamless {
  def paramless: Int = 1
  def callByName(f: => Int) = f
  def example: Int = callByName(paramless)
}

试试 new TestParamless().example:javap TestParamless(在 scala REPL 中)。

【讨论】:

  • @Freewind,是的。
  • 好的。我想跟进这件事。如果两者都是Function0,那么为什么两种情况都有不同的语法? def callByName[T](f: () => T) = f()f 之后需要 () 并且 callByName{ 1+ 1} 会给出语法错误。你需要() => 1 + 1
  • 为什么hello(() => "Freewind")无法编译?似乎=> String 的类型与() => String 不同
  • () => T 是从 Unit 到 T 的函数,而 => T 是按名称调用并具有 T 类型,在我看来它们看起来不同。
  • 我添加了更多细节 - 稍后我会尝试重新处理以使其更清晰。
猜你喜欢
  • 1970-01-01
  • 2011-09-08
  • 1970-01-01
  • 1970-01-01
  • 2020-10-05
  • 2011-06-21
  • 1970-01-01
  • 2011-01-06
  • 2020-03-01
相关资源
最近更新 更多