Scala函数文字类型问题

时间:2016-10-26 12:13:54

标签: scala currying

为什么executeCommand接受错误返回类型的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是严格的静态类型语言。 有人可以解释其背后的原因吗?

1 个答案:

答案 0 :(得分:4)

我认为这里发生的事情是Scala编译器中的两个独立机制正在协同工作:

丢弃值

这基本上只是一种避免在每次()是预期类型时必须明确写入Unit Unit值的方法。就像在while循环结束时,Unit返回方法,for(a <- list){ ... }表达式等

它重写(在编译期间在内存中,而不是在磁盘上)这样的代码

def foo: Unit = 42

到这个

def foo: Unit = { 42; () }

Eta扩张

这就是Scala中的方法转换为函数的方式。方法是JVM构造,是JVM类文件的元素。函数只是一个值,是scala.FunctionN类的一个实例。您可以将函数作为值传递(因为它们是值),但您不能将方法作为值传递(因为它们不是)。

基本上,编译器会进行以下转换:

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); () } )

您可以检查只有在将方法转换为函数时才会出现此“问题”:

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