Подтвердить что ты не робот

Scala "для понимания" с фьючерсами

Я читаю книгу Scala Cookbook (http://shop.oreilly.com/product/0636920026914.do)

Существует пример, связанный с будущим использованием, который подразумевает понимание.

До сих пор мое понимание для понимания заключается в том, что при использовании с коллекцией он будет производить другую коллекцию с тем же типом. Например, если каждый futureX имеет тип Future[Int], то также должно быть типа Future[Int]:

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

Может ли кто-нибудь объяснить мне, что именно происходит при использовании <- в этом коде? Я знаю, если бы он был генератором, он будет извлекать каждый элемент путем циклирования.

4b9b3361

Ответ 1

Сначала о понимании. На него много ответов было много раз, что это абстракция над несколькими монадическими операциями: map, flatMap, withFilter. Когда вы используете <-, scalac desugars это строки в монадические flatMap:

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

он выглядит как обязательное вычисление (что такое монада), вы привязываете результат вычисления к r. И часть yield снимается в вызов map. Тип результата зависит от типа monad.

Future trait имеет функции flatMap и map, поэтому мы можем использовать их для понимания. В вашем примере можно выделить следующий код:

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

Parallelism в стороне

Само собой разумеется, что если выполнение future2 зависит от r1, вы не можете избежать последовательного выполнения, но если будущие вычисления независимы, у вас есть два варианта. Вы можете обеспечить последовательное выполнение или разрешить параллельное выполнение. Вы не можете принудительно использовать последнее, поскольку контекст выполнения будет обрабатывать это.

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

всегда будет выполняться последовательно. Это может быть легко объяснено desugaring, после чего последующие вызовы computationReturningFutureX вызываются только внутри flatMaps.

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

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

может работать параллельно, а для понимания агрегирует результаты.

Ответ 2

Он позволяет r1, r2, r3 работать параллельно, если это возможно. Это может быть невозможно, в зависимости от того, сколько потоков доступно для выполнения будущих вычислений, но используя этот синтаксис, вы говорите компилятору, что это возможно, запускать эти вычисления параллельно, затем выполните yield(), когда все завершится.