Vector(1 to 10: _*)


#1

I’m reading Martin Oderski’s Programming In SCALA book and at one point he uses the contruction Vector(1 to 10: *) and I get what it does but I don’t get how "1 to 10:*" translates to the sequence 1 … 10. Can someone please explain.


#2

Perhaps the best way to answer this is with two lines in a REPL.

scala> 1 to 10
res0: scala.collection.immutable.Range.Inclusive = Range 1 to 10

scala> 1.to(10)
res1: scala.collection.immutable.Range.Inclusive = Range 1 to 10

The to method can be called on an Int and it takes an Int argument. The result is a Range that goes from the first value to the second value. Note the Range is a subtype of Seq, which is the fundamental supertype for things that can be indexed by integers like arrays.


#3

I think you might have meant Vector(1 to 10:_*), in which case:

:_* is the splat operator. From my understanding, it unpacks a sequence, usually so that the contents of that sequence can be used as arguments.

(1 to 10) creates a Range, and Vector(1 to 10:_*) “splats” that Range into a Vector.


#4

‘1 to 10’ basically is:

1.to(10)

So we are making a call to ‘to’ method which takes another Int as argument.
Result to this call is a Range.

But if you see documentation for Int, you won’t find ‘to’ method defined.

Under the hood, there is an implicit conversion from Int => RichInt.

It is RichInt which has ‘to’ method defined.


#5

I apologize. I left out the “_” in the body. Emily is correct the construction was Vector(1 to 10: _*). I under stand that 1 to 10 evaluates to Range 1 to 10 but I don’t understand this “splat” operator. I’m guessing it isn’t an actual operator (It doesn’t evaluate in the interpreter) but just a special piece of syntax that allows you to treat a Sequence as an argument list. It’s just odd that it was thrown in without an explanation.


#6

The way that I have generally thought of that “operator” is the following. Consider that you have a function def foo(nums: Double*) that you want to call with a sequence of Double. The normal calling syntax is clearly a type error, so you have to tell Scala that it should be treated differently. The colon in Scala is always used to specify types. The asterisk is clearly matched up with the one in the variable length argument declaration. The underscore is the general placeholder in Scala, and it makes the syntax nicer than having to actually specify the exact type. So when we call foo(xs:_*) I read it as call foo and treat xs as a something*.


#7

Thanks!


#8

The way this works under the hood is that the arguments passed to a function with * will be made into a Seq, and passed to the function.

This means that you can’t overload a method where one overload takes * and the other takes a Seq.

$ cat ar.scala
trait Example {
  def args(values: Int*): Unit
  def args(values: Seq[Int]): Unit
}
$ scalac ar.scala
ar.scala:3: error: double definition:
def args(values: Int*): Unit at line 2 and
def args(values: Seq[Int]): Unit at line 3
have same type after erasure: (values: Seq)Unit
  def args(values: Seq[Int]): Unit
      ^
one error found