简体   繁体   English

斯卡拉对期货的“理解”

[英]Scala's “for comprehension” with futures

I am reading through the Scala Cookbook ( http://shop.oreilly.com/product/0636920026914.do ) 我正在阅读斯卡拉食谱( http://shop.oreilly.com/product/0636920026914.do

There is an example related to Future use that involves for comprehension. 有一个与未来使用有关的例子涉及到理解。

So far my understanding about for comprehension is when use with a collection it will produce another collection with the same type. 到目前为止,我对理解的理解是,当与集合一起使用时,它将产生具有相同类型的另一个集合。 For example, if each futureX is of type Future[Int] , the following should also be of type Future[Int] : 例如,如果每个futureX的类型为Future[Int] ,则以下类型也应为Future[Int]类型:

for {
   r1 <- future1
   r2 <- future2
   r3 <- future3
} yield (r1+r2+r3)

Could someone explain me what exactly happening when use <- in this code? 有人可以解释一下使用<-在这段代码中究竟发生了什么吗? I know if it was a generator it will fetch each element by looping. 我知道如果它是一个生成器,它将通过循环获取每个元素。

First about for comprehension. 首先是为了理解。 It was answered on SO many many times, that it's an abstraction over a couple of monadic operations: map , flatMap , withFilter . 它在很多时候得到了回答,它是对几个withFilter操作的抽象: mapflatMapwithFilter When you use <- , scalac desugars this lines into monadic flatMap : 当你使用<- ,scalac将这些行desugar到monadic flatMap

r <- monad into monad.flatMap(r => ... ) r <- monad into monad.flatMap(r => ... )

it looks like an imperative computation (what a monad is all about), you bind a computation result to the r . 它看起来像一个命令式计算(monad是什么),你将计算结果绑定到r And yield part is desugared into map call. 并且yield部分被解雇到map呼叫中。 Result type depends on the type of monad 's. 结果类型取决于monad的类型。

Future trait has a flatMap and map functions, so we can use for comprehension with it. Future特征具有flatMapmap函数,因此我们可以使用它来理解它。 In your example can be desugared into the following code: 在您的示例中可以将以下代码置于:

future1.flatMap(r1 => future2.flatMap(r2 => future3.map(r3 => r1 + r2 + r3) ) )

Parallelism aside 抛开并行性

It goes without saying that if execution of future2 depends on r1 then you can't escape sequential execution, but if the future computations are independent, you have two choices. 不言而喻,如果future2执行依赖于r1则无法逃避顺序执行,但如果将来的计算是独立的,则有两种选择。 You can enforce sequential execution, or allow for parallel execution. 您可以强制执行顺序执行,也可以允许并行执行。 You can't enforce the latter, as the execution context will handle this. 您无法强制执行后者,因为执行上下文将处理此问题。

val res = for {
   r1 <- computationReturningFuture1(...)
   r2 <- computationReturningFuture2(...)
   r3 <- computationReturningFuture3(...)
} yield (r1+r2+r3)

will always run sequentially. 将始终按顺序运行。 It can be easily explained by the desugaring, after which the subsequent computationReturningFutureX calls are only invoked inside of the flatMaps, ie 它可以通过脱糖,在此之后,后续很容易解释computationReturningFutureX调用只有flatMaps内部调用,即

computationReturningFuture1(...).flatMap(r1 => 
    computationReturningFuture2(...).flatMap(r2 => 
        computationReturningFuture3(...).map(r3 => r1 + r2 + r3) ) )

However this is able to run in parallel and the for comprehension aggregates the results: 但是,这可以并行运行,并且for comprehension聚合结果:

val future1 = computationReturningFuture1(...)
val future2 = computationReturningFuture2(...)
val future3 = computationReturningFuture3(...)

val res = for {
   r1 <- future1
   r2 <- future2
   r3 <- future3
} yield (r1+r2+r3)

To elaborate those existing answers here a simple result to demonstrate how for comprehension works. 要细说了这些现有的答案,一个简单的结果,以说明如何for理解作品。

Its bit lengthy functions yet they worth taking look into it. 它有点冗长的功能,但值得深入研究。

A function that give us a range of integers 一个给出一系列整数的函数

scala> def createIntegers = Future{
             println("INT "+ Thread.currentThread().getName+" Begin.")
             val returnValue = List.range(1, 256)
             println("INT "+ Thread.currentThread().getName+" End.")
             returnValue
         }
createIntegers: createIntegers: scala.concurrent.Future[List[Int]]

A function that give us a range of chars 一个给我们一系列字符的函数

scala> def createAsciiChars = Future{
             println("CHAR "+ Thread.currentThread().getName+" Begin.")
             val returnValue = new ListBuffer[Char]
             for (i <- 1 to 256){
                  returnValue += i.toChar
             }
             println("CHAR "+ Thread.currentThread().getName+" End.")
             returnValue
          }
createAsciiChars: scala.concurrent.Future[scala.collection.mutable.ListBuffer[Char]]

Using these function calls within the for comprehension. for comprehension中使用这些函数调用。

scala> val result = for{
                        i <- createIntegers
                        s <- createAsciiChars
                    } yield i.zip(s)
       Await.result(result, Duration.Inf)
result: scala.concurrent.Future[List[(Int, Char)]] = Future(<not completed>)

For these below lines we can make out that all the function calls are synchronous ie createAsciiChars function call is not executed until createIntegers completes its execution. 对于以下这些行,我们可以看出所有函数调用都是同步的,即createAsciiChars函数调用在createIntegers完成执行之前不会执行。

scala> INT scala-execution-context-global-27 Begin.
       INT scala-execution-context-global-27 End.
       CHAR scala-execution-context-global-28 Begin.
       CHAR scala-execution-context-global-28 End.

Making these function createAsciiChars , createIntegers calls outside the for comprehensions will be asynchronous execution. 使这些函数createAsciiCharscreateIntegers调用之外的for comprehension将是异步执行。

It allows r1 , r2 , r3 to run in parallel, if possible. 如果可能,它允许r1r2r3并行运行。 It may not be possible, depending things like how many threads are available to execute Future computations, but by using this syntax you are telling the compiler to run these computations in parallel if possible, then execute the yield() when all have completed. 这可能是不可能的,这取决于有多少线程可用于执行Future计算,但是通过使用这种语法,您告诉编译器如果可能的话并行运行这些计算,然后在完成所有计算后执行yield()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM