猫效应 - 独立效果的平行组合

ami*_*ayh 9 functional-programming scala scala-cats cats-effect

我想组合多个IO应该并行独立运行的值.

val io1: IO[Int] = ???
val io2: IO[Int] = ???
Run Code Online (Sandbox Code Playgroud)

在我看来,我必须选择:

  1. 使用带有叉形连接图案的cats-effect纤维
    val parallelSum1: IO[Int] = for {
      fiber1 <- io1.start
      fiber2 <- io2.start
      i1 <- fiber1.join
      i2 <- fiber2.join
    } yield i1 + i2
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用Parallel实例IOparMapN(或它的兄弟姐妹等中的一种parTraverse,parSequence,parTupled等)
    val parallelSum2: IO[Int] = (io1, io2).parMapN(_ + _)
    
    Run Code Online (Sandbox Code Playgroud)

不确定每种方法的优缺点,何时我应该选择其中一种方法.当抽象出效果类型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(_ + _)
Run Code Online (Sandbox Code Playgroud)

Parallel类型类需要2层型构造,使得它稍微使用比较烦琐,没有上下文边界和与另外的模糊类型参数G[_]

感谢您的指导:)

Amitay

Yuv*_*kov 5

我想组合多个应该并行独立运行的 IO 值。

我的看法是,为了弄清楚“我什么时候使用哪个?”,我们需要返回旧的并行与并发讨论,这基本上归结为(引用已接受的答案):

并发是指两个或多个任务可以在重叠的时间段内启动、运行和完成。这并不一定意味着它们会同时运行。例如,单核机器上的多任务处理。

并行是指任务实际上是同时运行的,例如,在多核处理器上。

当我们进行类似 IO 的操作时,我们经常喜欢提供一个并发的例子,比如创建一个在线调用,或者与磁盘交谈。

问题是,当您说要“并行”执行时,您想要哪个,是前者还是后者?

如果我们指的是前者,那么使用Concurrent[F]两者都可以通过签名传达意图并提供正确的执行语义。如果是后者,例如,我们想要并行处理一组元素,那么使用 withParallel[F, G]将是更好的解决方案。

当我们考虑 this about 的语义时,通常会很困惑IO,因为它同时具有 forParallel和 的实例Concurrent,我们主要使用它来不透明地定义副作用操作。

作为旁注,Parallel采用两个一元类型构造函数的原因是因为M(in Parallel[M[_], F[_]]) 始终是一个Monad实例,我们需要一种方法来证明 Monad 也有一个Applicative[F]实例用于并行执行,因为当我们想到我们总是谈论顺序执行语义的 Monad。