为什么 Future[Set[Unit]] 不被接受为 Future[Unit]?

Why is Future[Set[Unit]] not accepted as Future[Unit]?

使用 futures 时的一个常见问题是,当您期望 Future[Unit] 时,甚至 Future[Future[Unit]] 也会被接受(参见 Why Shouldn’t You Use Future[Unit] as a Return Type in a Scala Program)。

我最近很惊讶Future.sequence(setOfFutures)在这种情况下不被接受:

import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global

val set = Set(Future(()))

def fuu: Future[Unit] = {
  Future.sequence(set)
}

使用 Scala 2.12.13 我得到错误:

type mismatch;

found : scala.concurrent.Future[scala.collection.immutable.Set[Unit]]

使用 Scala 2.13 我得到:

Cannot construct a collection of type Unit with elements of type Unit based on a collection of type scala.collection.immutable.Set[scala.concurrent.Future[Unit]].

当我将函数体更改为:

  val s = Future.sequence(set)
  s

我设置了和以前一样的错误。

为什么 Future[Future[Unit]] 被接受为 Future[Unit]Future[Set[Unit]]Future[List[Unit]] 不被接受?

考虑 Scala 2.13

Future.sequence 的签名
def sequence[A, CC[X] <: IterableOnce[X], To](in: CC[Future[A]])(
  implicit 
  bf: BuildFrom[CC[Future[A]], A, To], 
  executor: ExecutionContext
): Future[To]

如此给予

val set = Set(Future(()))
def fuu: Future[Unit] = Future.sequence(set)

然后推理将分配 sequence 的类型参数,就像这样

To = Unit
A = Unit
CC = Set

例如考虑 fuu 的 return 类型 Future[Unit] = Future[To]。因此我们有

def fuu: Future[Unit] = Future.sequence[Unit, Set, Unit](set)

所以编译器需要隐式分配bf参数

scala> implicitly[BuildFrom[Set[Future[Unit]], Unit, Unit]]
                 ^
       error: Cannot construct a collection of type Unit with elements of type Unit based on a collection of type Set[scala.concurrent.Future[Unit]].

现在考虑 Future.sequence

的 Scala 2.12 签名
def sequence[A, M[X] <: TraversableOnce[X]](in: M[Future[A]])(
  implicit 
  cbf: CanBuildFrom[M[Future[A]],A,M[A]],
  executor: ExecutionContext
): Future[M[A]]

如此给予

val set = Set(Future(()))
def fuu: Future[Unit] = Future.sequence(set)

推断变为

A = Unit
M = Set

所以我们有

def fuu: Future[Unit] = Future.sequence[Unit, Set](set)

其中编译器可以成功地隐式分配 cbf 参数

scala>  implicitly[CanBuildFrom[Set[Future[Unit]],Unit,Set[Unit]]]
res4: scala.collection.generic.CanBuildFrom[Set[scala.concurrent.Future[Unit]],Unit,Set[Unit]] = scala.collection.generic.GenSetFactory$$anon@1bff70a6

因此我们在 2.12 中实际上有以下情况

scala> def fuu: Future[Unit] = Future.sequence(set) : Future[Set[Unit]]
<console>:25: error: type mismatch;
 found   : scala.concurrent.Future[Set[Unit]]
 required: scala.concurrent.Future[Unit]
def fuu: Future[Unit] = Future.sequence(set) : Future[Set[Unit]]

这应该可以解释两个 Scala 版本之间的两个编译器错误消息之间的区别与值丢弃无关,而是与推理如何分配相应类型有关。