IO monad 如何使并发在 Scala 中变得容易?

How IO monad makes concurrency easy to do in Scala?

我看了这个 video 从 6 分 35 秒开始,它提到了这张图:

说 IO Monad 可以轻松处理并发。我对此感到困惑:它是如何工作的?两个for comprehension如何实现并发(df的计算)?

不,它不启用并发

for 理解只会帮助您省略几个括号和缩进。

您引用的代码,转换为 [flat]map 严格等同于:

async.boundedQueue[Stuff](100).flatMap{ a => 
  val d = computeB(a).flatMap{
    b => computeD(b).map{ result =>
      result
    }
  }

  val f = computeC(a).flatMap{ c =>
    computeE(c).flatMap{ e =>
      computeF(e).map{ result =>
        result
      }
    }
  }

  d.merge(f).map(g => g)
}

看,它只是帮你省略了几个括号和缩进(笑话)

并发隐藏在flatMapmap

一旦您理解了 for 是如何转换为 flatMapmap,您就可以在其中实现并发。

由于map接受一个函数作为参数,并不意味着该函数在map函数执行期间执行,您可以将函数推迟到另一个线程或运行 后者。这就是并发的实现方式。

PromiseFuture为例:

val future: Future = ```some promise```
val r: Future = for (v <- future) yield doSomething(v)
// or
val r: Future = future.map(v => doSomething(v))
r.wait

函数doSomethingFuture.map函数执行期间不执行,而是在promise提交时调用。

结论

如何使用for语法实现并发suger:

  1. for 将由 scala 编译器
  2. 转换为 flatMapmap
  3. 给你写flatMapmap,你会从参数中得到一个回调函数
  4. 随时随地调用你得到的函数

进一步阅读

许多语言的流程控制功能共享相同的 属性,它们就像定界延续 shift/reset,它们将以下执行捕获到一个函数中。

JavaScript:

async function() {
  ...
  val yielded = await new Promise((resolve) => shift(resolve))
                                  // resolve will captured execution of following statements upto end of the function.
  ...captured
}

Haskell:

do {
  ...
  yielded_monad <- ```some monad``` -- shift function is >>= of the monad
  ...captured
}

斯卡拉:

for {
  ...
  yielded_monad <- ```some monad``` // shift function is flatMap/map of the monad
  ...captured
} yield ...

下次当您看到一种语言功能将后续执行捕获到一个函数中时,您就知道您可以使用该功能实现流控制。

定界延续和call/cc之间的区别在于call/cc捕获程序的整个后续执行,但定界延续有一个范围。