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