12

我想组合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实例用于IOwith parMapN(或其同级之一,如,parTraverse等) parSequenceparTupled
    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(_ + _)

typeclassParallel需要 2 个类型构造函数,使用起来有点麻烦,没有上下文边界,并且有一个额外的 vague 类型参数G[_]

感谢您的指导:)

阿弥陀佛

4

1 回答 1

6

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

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

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

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

当我们执行类似 IO 的操作时,我们经常喜欢提供一个并发示例,例如创建一个通过有线调用或与磁盘通信。

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

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

当我们考虑 this 的语义时,常常会感到非常困惑IO,因为它有两个实例,Parallel并且Concurrent我们主要使用它来不透明地定义副作用操作。

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

于 2019-01-14T12:19:48.220 回答