List[OptionT[Future, Int]] 到 OptionT[Future, List[A]]

List[OptionT[Future, Int]] to OptionT[Future, List[A]]

我正在构建 IntList,使用异步计算来检索元素:

(1 to n).map(anAsyncThingy).toList

其中 anAsyncThingy returns OptionT[Future, Int]

因此结果的类型是 List[OptionT[Future, Int]]

我现在想要的是一个OptionT[Future, List[A]]

这是我迄今为止最好的尝试(我将添加一些存根,以便它可以 运行 在 REPL 中)

import scalaz._; import Scalaz._
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

def anAsyncThingy(x: Int): OptionT[Future, Int] = x.point[Future].liftM[OptionT]

val res = OptionT {
  Future.sequence {
    (1 to 3).map(anAsyncThingy(_).run).toList
  }.map(_.sequence)
}
res.map(println) // List(1, 2, 3)

以上按预期工作,但我觉得使用适当的 scalaz 构造有很大的改进空间,而不是从 monad 变换器中跳进跳出。

如何以更直接的方式获得相同的结果?

经过一些试验,我自己找到了答案:

val res = (1 to 3).map(anAsyncThingy).toList.sequenceU
res.map(println) // List(1, 2, 3)

为 scalaz 欢呼!

顺便说一句,需要 sequenceU 而不是 sequence 因为 scala 不够聪明,无法弄清楚你什么时候有

OptionT[M, A]

如果您修复类型参数(例如 MFuture

OptionT[Future, A]

它的形状是M[_]

编译器一直相信它具有 M[_, _] 形状,除非用勺子喂食(使用讨厌的 lambda 类型)

这里是 scalaz 的 sequenceU 介入并使用 Unapply 解决此问题的地方。有关该主题的更多信息 here

更新

根据 phadej 的评论,sequence ∘ map ≡ traverse,因此可以使用 traverseU:

使其更加简洁
(1 to 3).toList.traverseU(anAsyncThingy)

sequencesequenceU 的相同想法当然适用于 traversetraverseU