Reputation: 2385
yield
is mostly used in a for-yield loop to produce a new same-type collection. For example:
scala> val a = Array(2,3,5)
a: Array[Int] = Array(2, 3, 5)
scala> val result = for (elem <- a) yield 2 * elem
result: Array[Int] = Array(4, 6, 10)
This all works fine, the for loop takes an array and returns an array.
But then I noticed this:
scala> 1 to 10
res0: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
This generates a range type collection, but when you use this in conjunction with for-yield loop, this happened:
scala> for (i <- (1 to 10)) yield i + 2
res2: scala.collection.immutable.IndexedSeq[Int] = Vector(3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
Type that comes in is range, but the type it sends out is Vector. Why is this happenning? Am I missing anything?
Upvotes: 5
Views: 742
Reputation: 19167
Range has to have a fixed step between its values. Since it's impossible to infer that whatever yield returns will be a Range, the collection is made so that map
is defined as to return an IndexedSeq, i.e. behave like an IndexedSeq which it overrides.
Upvotes: 2
Reputation: 35970
No, you're not missing anything. Take a look at the signature for map
in Range.
def map[B](f: (A) ⇒ B): IndexedSeq[B]
That is why it's producing the values that you see. Range
itself "is a" IndexedSeq
.
Why do I talk about map
when discussing a for-comprehension? For comprehensions are syntactic sugar for compiler transformations which utilizie map
, flatMap
and filter
under the hood (amongst other things.) So even if you just yield what you put in, you're calling a map
with identity
.
Also note, as to the Vector
portion of why this would happen...
IndexedSeq
is a trait. If you were to look at the source code for this trait here, the companion object produces a Vector
from the newBuilder[A]
method:
object IndexedSeq extends SeqFactory[IndexedSeq] {
override lazy val ReusableCBF =
scala.collection.IndexedSeq.ReusableCBF.asInstanceOf[GenericCanBuildFrom[Nothing]]
class Impl[A](buf: ArrayBuffer[A]) extends AbstractSeq[A] with IndexedSeq[A] with Serializable {
def length = buf.length
def apply(idx: Int) = buf.apply(idx)
}
def newBuilder[A]: Builder[A, IndexedSeq[A]] = Vector.newBuilder[A]
implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, IndexedSeq[A]] =
ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]]
}
Upvotes: 4
Reputation: 28511
Have a look at Range
:
class Range extends AbstractSeq[Int] with IndexedSeq[Int] with CustomParallelizable[Int, ParRange] with Serializable
Then the signature of map
:
def map[B](f: (A) ⇒ B): IndexedSeq[B]
The reason for this is that Range
is actually a sugared IndexedSeq
, all it adds on top is range specific behaviour:
Range.Inclusive
, Range.Exclusive
etc.
The reason why map
returns an IndexedSeq
is likely a compiler limitation, as it cannot predict the type of the Range
that results from the map
operation.
Upvotes: 4