#scala #types #type-conversion
Вопрос:
Распространенная ошибка при работе с фьючерсами заключается в том , что, когда вы ожидаете Future[Unit]
, даже Future[Future[Unit]]
будет принято (см., Например, Почему Вы не должны использовать Future[Unit] в качестве типа возврата в программе Scala).
Я был удивлен, что в последнее 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.одновременно.Будущее[scala.коллекция.неизменяемая.Набор[Единица измерения]]
С помощью Scala 2.13 я получаю:
Невозможно создать коллекцию единиц типа с элементами единицы типа на основе коллекции типа scala.collection.immutable.Установите[scala.одновременный.Будущее[Единица измерения]].
Когда я изменяю тело функции на:
val s = Future.sequence(set)
s
Я получаю ту же ошибку, что и раньше.
Почему Future[Future[Unit]]
принимается как Future[Unit]
и Future[Set[Unit]]
или Future[List[Unit]]
нет?
Ответ №1:
Рассмотрим подпись Future.sequence
в Scala 2.13
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]].
Теперь рассмотрим Scala 2.12 подпись Future.sequence
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, связанную не с отбрасыванием значений, а с тем, как вывод назначил соответствующие типы.
Комментарии:
1. Теперь я понимаю. Предпосылка моего вопроса неверна:
Future[Future[Unit]]
не принимается какFuture[Unit]
. ВместоFuture(Future(()))
этого он переписывается какFuture(Future(()), ())
, когда определяется, гдеFuture[Unit]
ожидается.2. Примечание: похоже, что значение, исключающее перезапись, изменилось в Scala 3, так как при этом код
def foo: Future[Unit] = Future(Future(()))
больше не принимается и выдает найденную ошибку: scala.concurrent. Требуется будущая[Единица измерения]: Единица измерения.3. Я нашел ответ неудовлетворительным, потому что не видел движущихся частей. Он терпит неудачу, потому что у него нет строителя
BuildFrom[Set[Future[Unit]], Unit, Unit]
. Я могу это реализовать. Может быть, он запускает все в специальном контексте в качестве побочного эффекта. Я понимаю, что ОП спрашивает, почему каким-то образом не срабатывает отбрасывание ценности.