使用匿名参数时,将来的映射会透明地分为两个函数

时间:2014-05-20 12:37:30

标签: scala future

我对Scala期货所观察到的事情感到困惑,我无法弄清楚这里会发生什么。我将未来映射到另一个结果,没有什么花哨的:

import scala.concurrent.{Await, Future}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._

object Example extends App {
  val f = Future {
    Thread.sleep(1000)
    println("Future: " + Thread.currentThread + " (" + 
        System.currentTimeMillis + ")")
    "future"
  }
  val g = f map { x =>
    println("Map future: " + Thread.currentThread + 
        " (" + System.currentTimeMillis + ")")
    "mapped " + x
  }
  println(Await.result(g, 5 seconds))
}

由于我将未来映射到的函数取决于f的结果,我希望该函数仅在f的计算完成后触发。这也是我用上面的代码可以观察到的。但是,当我更改g的函数以携带匿名参数时,此行为会更改:

val g = f map {
  println("Map future: " + Thread.currentThread + " (" + 
      System.currentTimeMillis + ")")
  "mapped " + _
}

即使g的结果取决于f的结果,也会在f的计算完成之前触发该方法。看起来好像该方法在实际需要输入值之前和之后被拆分为一部分。此外,线程的第一部分在main中执行。这是编译器在这里做的事吗?对我来说,这非常出乎意料。或者这种行为是否在Scala的某处表达过?但是我无法在消息来源中找到它。

有人知道这里发生了什么吗?我正在使用Scala 2.10。

1 个答案:

答案 0 :(得分:10)

它可能会让你感到惊讶,但是在lambda构建期间执行println:

List(1,2,3).map { 
    println("Hi!")
    "Processing " + _ 
}

// Hi! 
// res4: List[String] = List(Processing 1, Processing 2, Processing 3)

正如您所看到的,Hi!只被输出一次。

这是因为您的代码在语义上与:

相同
def constructLambda() = {
  // do something, e.g. println
  // now return actual lambda
}
val lambda = constructLambda()
xs map (lambda) // now trigger previously *lazy* piece of code
// or even this, like in your snippet
xs map lambda