Scala 函数文字类型问题
Scala Function Literals Type issue
为什么executeCommand
接受了错误return类型的callBack
函数?
import scala.sys.process._
object Test {
def executeCommand(x: String)(y: Int => Unit) = {
def matchStrToInt(str: String) = {
str match {
case "true" => 1
case "false" => 2
}
}
y(matchStrToInt(x))
} //> executeCommand: (x: String)(y: Int => Unit)Unit
executeCommand("true")(callBack)
def callBack(x: Int): Int = {
x
} //> callBack: (x: Int)Int
}
据我所知,Scala 是严格的静态类型语言。
有人可以解释一下背后的原因吗?
我认为这里发生的是 Scala 编译器中的两个独立机制协同工作:
值丢弃
这基本上只是一种避免显式写入 ()
、Unit
值的方法,每次 Unit
都是预期类型。就像在 while
循环结束时,Unit
返回方法,for(a <- list){ ... }
表达式等
它重写(在编译期间在内存中,而不是在磁盘上)这样的代码
def foo: Unit = 42
至此
def foo: Unit = { 42; () }
埃塔膨胀
这就是 Scala 中的 方法 转换为 函数 的方式。方法是 JVM 构造,是 JVM class 文件的元素。函数只是一个值,scala.FunctionN
class 的一个实例。您可以将函数作为值传递(因为它们是值),但您不能将方法作为值传递(因为它们不是)。
所以基本上编译器会进行以下转换:
def foo(i: Int): Int = i
def bar(f: Int => Int) = f
bar(foo)
~~~>
bar( (x1: Int) => foo(x1) )
把两者放在一起
所以当你有这个代码时:
def foo(i: Int): Int = i
def bar(f: Int => Unit) = f
bar(foo)
编译器会先使用eta展开将foo
转为函数
bar( (x1: Int) => foo(x1) )
然后它会看到 foo(x1)
是一个 Int
类型的表达式,而那里应该是一个 Unit
类型的表达式。所以它将应用值丢弃。
bar( (x1: Int) => { foo(x1); () } )
你可以检查这个"problem"只发生在将方法转换为函数时:
scala> def bar(f: Int => Unit) = f
bar: (f: Int => Unit)Int => Unit
scala> val foo = (i: Int) => i
foo: Int => Int = <function1>
scala> bar(foo)
<console>:14: error: type mismatch;
found : Int => Int
required: Int => Unit
bar(foo)
^
为什么executeCommand
接受了错误return类型的callBack
函数?
import scala.sys.process._
object Test {
def executeCommand(x: String)(y: Int => Unit) = {
def matchStrToInt(str: String) = {
str match {
case "true" => 1
case "false" => 2
}
}
y(matchStrToInt(x))
} //> executeCommand: (x: String)(y: Int => Unit)Unit
executeCommand("true")(callBack)
def callBack(x: Int): Int = {
x
} //> callBack: (x: Int)Int
}
据我所知,Scala 是严格的静态类型语言。 有人可以解释一下背后的原因吗?
我认为这里发生的是 Scala 编译器中的两个独立机制协同工作:
值丢弃
这基本上只是一种避免显式写入 ()
、Unit
值的方法,每次 Unit
都是预期类型。就像在 while
循环结束时,Unit
返回方法,for(a <- list){ ... }
表达式等
它重写(在编译期间在内存中,而不是在磁盘上)这样的代码
def foo: Unit = 42
至此
def foo: Unit = { 42; () }
埃塔膨胀
这就是 Scala 中的 方法 转换为 函数 的方式。方法是 JVM 构造,是 JVM class 文件的元素。函数只是一个值,scala.FunctionN
class 的一个实例。您可以将函数作为值传递(因为它们是值),但您不能将方法作为值传递(因为它们不是)。
所以基本上编译器会进行以下转换:
def foo(i: Int): Int = i
def bar(f: Int => Int) = f
bar(foo)
~~~>
bar( (x1: Int) => foo(x1) )
把两者放在一起
所以当你有这个代码时:
def foo(i: Int): Int = i
def bar(f: Int => Unit) = f
bar(foo)
编译器会先使用eta展开将foo
转为函数
bar( (x1: Int) => foo(x1) )
然后它会看到 foo(x1)
是一个 Int
类型的表达式,而那里应该是一个 Unit
类型的表达式。所以它将应用值丢弃。
bar( (x1: Int) => { foo(x1); () } )
你可以检查这个"problem"只发生在将方法转换为函数时:
scala> def bar(f: Int => Unit) = f
bar: (f: Int => Unit)Int => Unit
scala> val foo = (i: Int) => i
foo: Int => Int = <function1>
scala> bar(foo)
<console>:14: error: type mismatch;
found : Int => Int
required: Int => Unit
bar(foo)
^