为啥 Future[Set[Unit]] 不被接受为 Future[Unit]?

Posted

技术标签:

【中文标题】为啥 Future[Set[Unit]] 不被接受为 Future[Unit]?【英文标题】:Why is Future[Set[Unit]] not accepted as Future[Unit]?为什么 Future[Set[Unit]] 不被接受为 Future[Unit]? 【发布时间】:2021-08-15 13:00:04 【问题描述】:

使用期货时的一个常见问题是,当您期望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 我得到错误:

类型不匹配;

找到:scala.concurrent.Future[scala.collection.immutable.Set[Unit]]

使用 Scala 2.13 我得到:

无法基于 scala.collection.immutable.Set[scala.concurrent.Future[Unit]] 类型的集合构造具有 Unit 类型元素的 Unit 类型集合。

当我将函数的主体更改为:

  val s = Future.sequence(set)
  s

我得到了和以前一样的错误。

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

【问题讨论】:

【参考方案1】:

考虑 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 的返回类型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$1@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 版本之间的两个编译器错误消息之间的差异与丢弃值无关,而是与推理如何分配相应的类型有关。

【讨论】:

我现在明白了。我的问题的前提是错误的:Future[Future[Unit]] 不被接受为Future[Unit]Future(Future(())) 被重写为 Future(Future(()), ()),而不是在预期 Future[Unit] 的位置定义。 注意:似乎放弃重写的值在 Scala 3 中发生了变化,因为它的代码 def foo: Future[Unit] = Future(Future(())) 不再被接受并给出错误 Found: scala.concurrent.Future[Unit]必需:单位. 我发现答案不令人满意,因为我没有看到活动部件。它失败了,因为它没有构建器BuildFrom[Set[Future[Unit]], Unit, Unit]。我可以实现它。也许它会在特殊的上下文中运行所有内容作为副作用。我知道 OP 在问为什么不重视丢弃以某种方式起作用。

以上是关于为啥 Future[Set[Unit]] 不被接受为 Future[Unit]?的主要内容,如果未能解决你的问题,请参考以下文章

为啥从主线程调用时,`std::promise::set_value` 会抛出错误?

为啥 asyncio.Future 与 concurrent.futures.Future 不兼容?

为啥 Future(1) 在回复和编译程序之间返回不同的结果?

为啥 ExecutorService.submit(Runnable task) 返回 Future<?> 而不是 Future<Void>?

为啥 Python 中的 future 语句需要放在一切之前?

为啥 Future.Builder 中的快照从不出错?