为啥 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>?