使用无标签的最终版本(不使用IO,而是使用通用F),我如何抽象这样的内容:
def doSomething(s: String): IO[Unit] = ???
List("authMethods", "secretEngines", "plugins", "CAs", "common").parTraverse(doSomething)
我能得到的最接近的是使用Concurrent对象中的parTraverseN
,但是我假设它将并发运行,而不是并行运行(如parallelism)。这也迫使我选择n
,而parTraverse
则没有。
列表的大小只是一个例子,它可能更大。 doSomething
是一个纯函数,它的多个执行可以并行运行而不会出现问题。
理想情况下,考虑到doSomething
返回IO[Unit]
,我想将parTraverse_
抽象为具有正确类型类实例的F
。
这是一个类似的完整工作示例:
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
那样指定边界实际上不是一件好事,并且可能会导致过多的内存使用等。(但这取决于例如列表的预期大小和doSomething
正在做的工作类型,并且可能不在问题范围内。]
[第二个脚注是,在Parallel
类型类的意义上,“并行”比Cats“并发基础”文档中的并行与并行区分中的“并行”更笼统。例如,Parallel
类型类对一种非常通用的逻辑并行性进行建模,该逻辑并行性也包含error accumulation。所以当你写:
我假设它将并发运行而不是并行运行(如parallelism中的。
…您的假设是正确的,但不完全是因为parTraverseN
方法位于Concurrent
而非Parallel
上;请注意,Concurrent.parTraverseN
仍需要一个Parallel
实例。当您在par
的上下文中看到Parallel
或cats.effect.Concurrent
类型类时,应该考虑并发,而不是“并发基础”意义上的“并行性”。