Эффект кошки - параллельная композиция независимых эффектов - PullRequest
0 голосов
/ 13 января 2019

Я хочу объединить несколько IO значений, которые должны работать независимо параллельно.

val io1: IO[Int] = ???
val io2: IO[Int] = ???

На мой взгляд, у меня есть варианты:

  1. Используйте волокна с эффектом кошки с рисунком вилки
    val parallelSum1: IO[Int] = for {
      fiber1 <- io1.start
      fiber2 <- io2.start
      i1 <- fiber1.join
      i2 <- fiber2.join
    } yield i1 + i2
    
  2. Используйте экземпляр Parallel для IO с parMapN (или одним из его братьев и сестер, таких как parTraverse, parSequence, parTupled и т. Д.)
    val parallelSum2: IO[Int] = (io1, io2).parMapN(_ + _)
    

Не уверен насчет плюсов и минусов каждого подхода, и когда я должен выбрать один из других. Это становится еще сложнее, если абстрагироваться от типа эффекта IO (стиль без тега):

def io1[F[_]]: F[Int] = ???
def io2[F[_]]: F[Int] = ???

def parallelSum1[F[_]: Concurrent]: F[Int] = for {
  fiber1 <- io1[F].start
  fiber2 <- io2[F].start
  i1 <- fiber1.join
  i2 <- fiber2.join
} yield i1 + i2

def parallelSum2[F[_], G[_]](implicit parallel: Parallel[F, G]): F[Int] =
  (io1[F], io2[F]).parMapN(_ + _)

Класс типов Parallel требует двух конструкторов типов, что делает его несколько более громоздким в использовании без границ контекста и с дополнительным параметром неопределенного типа G[_]

Ваше руководство ценится:)

Amitay

1 Ответ

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

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

То, как я это вижу, чтобы выяснить, «когда я использую что?», Нам нужно вернуть старую параллельную и параллельную дискуссию , которая в основном сводится к (цитируя принятые ответ):

Параллельность - это когда две или более задач могут запускаться, запускаться и завершаться в перекрывающиеся периоды времени. Это не обязательно означает, что они когда-либо оба будут работать одновременно . Например, многозадачность на одноядерный станок.

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

Нам часто хотелось бы привести пример параллелизма, когда мы выполняем операции, подобные вводу-выводу, такие как создание беспроводного вызова или разговор с диском.

Вопрос в том, какой из них вы хотите, когда говорите, что хотите выполнить «параллельно», это первый или второй?

Если мы ссылаемся на первое, то использование Concurrent[F] передает намерение по сигнатуре и обеспечивает правильную семантику выполнения. Если это последнее, и мы, например, хотим обрабатывать набор элементов параллельно, то лучше было бы использовать Parallel[F, G].

Часто это довольно запутанно, когда мы думаем о семантике этого относительно IO, потому что у него есть оба экземпляра для Parallel и Concurrent, и мы в основном используем его для непрозрачного определения побочных эффектов.

В качестве примечания, причина, по которой Parallel принимает два конструктора унарного типа, заключается в том, что MParallel[M[_], F[_]]) всегда в экземпляре Monad, и нам нужен способ доказать Monad также имеет экземпляр Applicative[F] для параллельных выполнений, потому что когда мы думаем о Monad, мы всегда говорим о семантике последовательного выполнения.

...