【问题标题】: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]] 不被接受?

【问题讨论】:

    标签: scala types type-conversion


    【解决方案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 在问为什么不重视丢弃以某种方式起作用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-15
    • 1970-01-01
    • 2019-12-28
    • 2015-08-19
    • 2017-10-13
    相关资源
    最近更新 更多