Reputation: 92170
Following Scala courses on Coursera, Martin Odersky showed an example code which is:
1 to 5 map ( i => i*i )
And he said the Range
gets transformed to a Vector
because they share the same interface (IndexedSeq
) and the result could not be represented as a Range
(it was more clear in its example since he generated a pair which is not representable as a Range
).
I'm not sure to understand because I think he said previously that in a for expression the 1st generator will determine the kind of element that will be yielded, and it seems not always true, at least for Range
.
And I'm not sure to understand why the output is Vector
, because Vector
may not be the only other one implementation that can represent the result computed above.
Can someone help me understand this part please?
Upvotes: 9
Views: 1262
Reputation: 167901
map
secretly takes a CanBuildFrom
as an implicit argument. Its job is to produce a new collection given the one you've already got (and the type of the contents). Since Range
can't contain arbitrary stuff--not even arbitrary integers--there is no CanBuildFrom
that produces a Range
. The most specific supertype of Range
that does have a CanBuildFrom
is IndexedSeq
. The collection that is actually built by this is a Vector
.
Upvotes: 10
Reputation: 9762
As I'm sure Martin also explained, for
comprehensions correspond to (are translated into) chained invocations of the map
and flatMap
methods (and foreach
if you don't use yield
).
The reason why it generally results in a value of the type of the first generator is that map
and flatMap
generally return the same type as their receiver (map
on a List
returns a List
, etc.).
Now the problem with Range
s is that they cannot represent things that are not regular sequences of integers. As a consequence, the return type of map
and flatMap
as defined for Range
cannot be Range
. The next best match is Vector
, the prototypical implementation of an indexed sequence.
(If you look at the source code or even the Scala doc page I linked to, you will see that it is a little more complicated that just the return type, but conceptually, that is the reason. Edit: ...and now Rex Kerr just dropped the CanBuildFrom
bomb.)
Upvotes: 2
Reputation: 7320
Vector
is the default implementation for IndexedSeq
. The map
cannot be represented as a Range
since the Range
class is designed to contain a series of numbers that can be represented by a start, stop, and step value (similar to range
in Python). The API docs specify that it's a special case of IndexSeq
.
We can see 1 to 5 map { i => i * i }
will get us a container of values (1, 4, 9, 16, 25). We can get a start and stop, but no constant step value.
Upvotes: 1