parSequence и parTraverse в финале без тегов - PullRequest
3 голосов
/ 29 мая 2020

Используя final без тегов (без использования ввода-вывода, а скорее как generi c F), как я могу абстрагироваться от чего-то вроде этого:

def doSomething(s: String): IO[Unit] = ???

List("authMethods", "secretEngines", "plugins", "CAs", "common").parTraverse(doSomething)

Самое близкое, что я могу получить, это использование parTraverseN из Параллельный объект, но я предполагаю, что он будет работать одновременно, а не параллельно (как в parallelism ). Это также заставляет меня выбирать n, а parTraverse - нет.

Размер списка - это просто пример, он может быть намного больше. doSomething - чистая функция, несколько ее выполнений могут выполняться параллельно без проблем.

В идеале, учитывая, что doSomething возвращает IO[Unit], я хотел бы абстрагироваться от parTraverse_ до F с правильным экземпляром класса типов.

1 Ответ

5 голосов
/ 29 мая 2020

Вот аналогичный полный рабочий пример:

import cats.Applicative
import cats.instances.list._
import cats.syntax.foldable._

trait Service[F[_]] {
  val items = List("authMethods", "secretEngines", "plugins", "CAs", "common")

  def doSomething(s: String): F[Unit] = ???

  def result(implicit F: Applicative[F]): F[Unit] =
    items.traverse_(doSomething)
}

Если вы хотите использовать здесь parTraverse_, минимальные необходимые изменения будут выглядеть примерно так:

import cats.{Applicative, Parallel}
import cats.instances.list._
import cats.syntax.parallel._

trait Service[F[_]] {
  val items = List("authMethods", "secretEngines", "plugins", "CAs", "common")

  def doSomething(s: String): F[Unit] = ???

  def result(implicit F: Applicative[F], P: Parallel[F]): F[Unit] =
    items.parTraverse_(doSomething)
}

В качестве альтернативы вы можно использовать Parallel.parTraverse_(items)(doSomething) и пропустить импорт syntax. Для обоих подходов требуется экземпляр Foldable для List (предоставленный здесь импортом cats.instances.list._, который больше не потребуется в Cats 2.2.0 ), и экземпляр Parallel для F, который вы получаете через ограничение P.

(Обратите внимание, что ограничение Applicative на result больше не требуется во второй версии, но это только потому, что это очень простой пример - Я предполагаю, что ваш реальный код полагается на что-то вроде Sync, и ему понадобится и это, и Parallel.)

Однако для этого ответа нужна пара сносок. Во-первых, это может быть не очень хорошо, что parTraverse_ не заставляет вас указывать привязку, как это делает parTraverseN, и может привести к чрезмерному использованию памяти, et c. (но это будет зависеть, например, от ожидаемого размера ваших списков и вида работы, которую выполняет doSomething, и, вероятно, выходит за рамки вопроса).

Вторая сноска - это «параллель» в Смысл класса типа Parallel является более общим, чем «параллель» в различении параллельного и параллельного в документе Cats «Основы параллелизма». Класс типа Parallel моделирует очень общий тип логического параллелизма c, который также включает, например, накопление ошибок . Поэтому, когда вы пишете:

, я предполагаю, что это будет работать одновременно, а не параллельно (как в parallelism ).

... ваше предположение верно , но не совсем потому, что метод parTraverseN находится на Concurrent вместо Parallel; обратите внимание, что для Concurrent.parTraverseN по-прежнему требуется экземпляр Parallel. Когда вы видите par или класс типов Parallel в контексте cats.effect.Concurrent, вы должны думать о параллелизме, а не о «параллелизме» в смысле «Основы параллелизма».

...