8

Why can I use to to construct a Range for the first argument of Future.traverse, but not until? See the following example Scala console interaction.

scala> Future.traverse(1 to 5)(Future.successful)
val res5: scala.concurrent.Future[IndexedSeq[Int]] = Future(<not completed>)

scala> Future.traverse(1 until 5)(Future.successful)
                                 ^
       error: Cannot construct a collection of type scala.collection.immutable.AbstractSeq[Int] with elements of type Int based on a collection of type scala.collection.immutable.AbstractSeq[Int].

scala> res5
val res7: scala.concurrent.Future[IndexedSeq[Int]] = Future(Success(Vector(1, 2, 3, 4, 5)))

scala>

Note that I'm using Scala 2.13.5 for the console though Scala 2.13.2 seems to have the same behavior.

For what it's worth, I noticed that to returns Range.Inclusive, and until returns Range.Exclusive. But both extend Range, so I'm at a loss as to what is different between these two types such that Future.traverse can take one but not the other as the first argument.

Tom Wang
  • 877
  • 1
  • 9
  • 15
  • 3
    It seems to be related to an [open bug](https://github.com/scala/bug/issues/6948) reported years ago. The issue appears to be gone in Scala 3. – Leo C Jun 02 '21 at 18:23
  • 3
    For what it's worth, an explicit type signature will fix it. `Future.traverse[Int, Int, IndexedSeq](1 until 5)(Future.successful)` works. – Silvio Mayolo Jun 02 '21 at 18:25
  • Cute. Thanks! Looks like the type signature of `Range` starting with extending `AbstractSeq` rather than `IndexedSeq` actually made a difference. – Tom Wang Jun 02 '21 at 18:37
  • @LeoC Yes, I think you're right on that one! – Tom Wang Jun 02 '21 at 18:37

1 Answers1

6

This looks like a combination of several problems.

1 until 5 returns a scala.collection.immutable.Range, while 1 to 5 returns Range.Inclusive.

scala> val exclusive: Range.Exclusive = 1 until 5
                                          ^
       error: type mismatch;
        found   : scala.collection.immutable.Range
        required: Range.Exclusive

scala> val inclusive: Range.Inclusive = 1 to 5
val inclusive: Range.Inclusive = Range 1 to 5

Range.Inclusive is a concrete class, and scala.collection.immutable.Range is the parent of both Range.Inclusive and Range.Exclusive. It looks as though due to the open bug Leo C mentioned, scala.collection.immutable.Range considered only AbstractSeq rather than IndexedSeq (as Silvio Mayolo's comment suggested). And AbstractSeq cannot be constructed. Range.Inclusive, being a concrete class, does not have the same problem.

I am not sure at this point why AbstractSeq cannot be constructed. I had thought the reason was that AbstractSeq is abstract, but IndexedSeq is a trait as well. That said, it is probably by design that AbstractSeq cannot be constructed, but IndexedSeq can.

Tom Wang
  • 877
  • 1
  • 9
  • 15