Как монада IO делает параллелизм простым в Scala? - PullRequest
0 голосов
/ 08 января 2019

Я смотрю это видео и начинается в 6min35s, там упоминается этот график:

enter image description here

сказав, что IO Monad облегчает параллелизм. Я запутался в этом: как это работает? Как два for comprehension включают параллелизм (вычисление d и f)?

1 Ответ

0 голосов
/ 08 января 2019

Нет, это не включает параллелизм

for Понимание только поможет вам опустить несколько скобок и отступов.

Код, который вы указали, переводите [flat]map строго эквивалентно:

async.boundedQueue[Stuff](100).flatMap{ a => 
  val d = computeB(a).flatMap{
    b => computeD(b).map{ result =>
      result
    }
  }

  val f = computeC(a).flatMap{ c =>
    computeE(c).flatMap{ e =>
      computeF(e).map{ result =>
        result
      }
    }
  }

  d.merge(f).map(g => g)
}

Видите, это поможет вам опустить несколько скобок и отступов (шутка)

Параллельность скрыта в flatMap и map

Как только вы поймете, как for переводится в flatMap и map, вы можете реализовать свой параллелизм внутри них.

Поскольку map принимает функцию в качестве аргумента, это не означает, что функция выполняется во время выполнения функции map, вы можете перенести функцию на другой поток или запустить ее последним. Вот как реализован параллелизм.

Взять Promise и Future как пример:

val future: Future = ```some promise```
val r: Future = for (v <- future) yield doSomething(v)
// or
val r: Future = future.map(v => doSomething(v))
r.wait

Функция doSomething не выполняется во время выполнения функции Future.map, вместо этого она вызывается при фиксации promise.

Заключение

Как реализовать параллелизм с использованием синтаксиса for:

  1. for преобразуется в flatMap и map компилятором scala
  2. Напишите вам flatMap и map, где вы получите функцию обратного вызова из аргумента
  3. Вызывайте функцию, которую вы получили, когда и где хотите

Дальнейшее чтение

Функция управления потоком во многих языках имеет одно и то же свойство, они похожи на продолжение с разделителями shift/reset, где они фиксируют следующее выполнение до области видимости в функцию.

JavaScript:

async function() {
  ...
  val yielded = await new Promise((resolve) => shift(resolve))
                                  // resolve will captured execution of following statements upto end of the function.
  ...captured
}

Haskell:

do {
  ...
  yielded_monad <- ```some monad``` -- shift function is >>= of the monad
  ...captured
}

Scala:

for {
  ...
  yielded_monad <- ```some monad``` // shift function is flatMap/map of the monad
  ...captured
} yield ...

в следующий раз, когда вы увидите языковую функцию, которая фиксирует выполнение после выполнения функции, вы знаете, что можете реализовать управление потоком с помощью этой функции.

Разница между продолжением с разделителями и call / cc заключается в том, что call / cc захватывает все последующее выполнение программы, но продолжение с разделителями имеет область действия.

...