240

I have the following piece of code from this question:

def addChild(n: Node, newChild: Node) = n match {
  case Elem(prefix, label, attribs, scope, child @ _*) => Elem(prefix, label, attribs, scope, child ++ newChild : _*)
  case _ => error("Can only add children to elements!")
}

Everything in it is pretty clear, except this piece: child ++ newChild : _*

What does it do?

I understand there is Seq[Node] concatenated with another Node, and then? What does : _* do?

philantrovert
  • 9,904
  • 3
  • 37
  • 61
amorfis
  • 15,390
  • 15
  • 77
  • 125

4 Answers4

175

It "splats"1 the sequence.

Look at the constructor signature

new Elem(prefix: String, label: String, attributes: MetaData, scope: NamespaceBinding,
         child: Node*)

which is called as

new Elem(prefix, label, attributes, scope,
         child1, child2, ... childN)

but here there is only a sequence, not child1, child2, etc. so this allows the result sequence to be used as the input to the constructor.


1 This doesn't have a cutesy-name in the SLS, but here are the details. The important thing to get is that it changes how Scala binds the arguments to the method with repeated parameters (as denoted with Node* above).

The _* type annotation is covered in "4.6.2 Repeated Parameters" of the SLS.

The last value parameter of a parameter section may be suffixed by “*”, e.g. (..., x:T ). The type of such a repeated parameter inside the method is then the sequence type scala.Seq[T]. Methods with repeated parameters T * take a variable number of arguments of type T . That is, if a method m with type (p1 : T1, . . . , pn : Tn,ps : S)U is applied to arguments (e1, . . . , ek) where k >= n, then m is taken in that application to have type (p1 : T1, . . . , pn : Tn,ps : S, . . . , ps0S)U, with k ¡ n occurrences of type S where any parameter names beyond ps are fresh. The only exception to this rule is if the last argument is marked to be a sequence argument via a _ type annotation. If m above is applied to arguments (e1, . . . , en,e0 : _), then the type of m in that application is taken to be (p1 : T1, . . . , pn : Tn,ps :scala.Seq[S])**

Nimantha
  • 6,405
  • 6
  • 28
  • 69
107
  • child ++ newChild - sequence
  • : - type ascription, a hint that helps compiler to understand, what type does that expression have
  • _* - placeholder accepting any value + vararg operator

child ++ newChild : _* expands Seq[Node] to Node* (tells the compiler that we're rather working with a varargs, than a sequence). Particularly useful for the methods that can accept only varargs.

Vasil Remeniuk
  • 20,519
  • 6
  • 71
  • 81
  • 2
    Could you write more about "type ascription"? What is it and how does it work? – amorfis May 19 '11 at 07:47
  • 11
    http://stackoverflow.com/questions/2087250/what-is-the-purpose-of-type-ascription-in-scala – Vasil Remeniuk May 19 '11 at 09:52
  • 2
    Great answer. So by writing `a: _*`, you are telling the compiler to treat `a` as an instance of `_*`, which in this case just `Node*` – Ming Aug 12 '21 at 20:10
39

All the above answer looks great, but just need a sample to explain this . Here it is :

val x : Seq[Seq[Int]] = Seq(Seq(1),Seq(2))

def f(arg: Seq[Any]*) : Int = {
 arg.length
}
f(x) //1 as x is taken as single arg
f(x:_*)  // 2 as x is "unpacked" as a Seq[Any]*

So now we know what :_* do is to tell compiler : please unpack this argument and bind those elements to the vararg parameter in function call rather than take the x as a single argument .

So in a nutshell, the :_* is to remove ambiguity when pass argument to vararg parameter.

Keith
  • 669
  • 6
  • 16
  • 1
    This is the answer I was looking for. The other ones are great, but this one with this simple example, made my day. Hat tip to @Keith – George Fandango Nov 17 '20 at 15:21
  • This is also a great example for another way: This will unpack to two items: `Seq(1)` and `Seq(2)`. How would I unpack to `1` and `2`? Can I do `f(x:_*:_*)`? – Mike Williamson Dec 07 '22 at 17:01
12

For some of the lazy folks like me, it just converts collection values to varArgs!

mani_nz
  • 4,522
  • 3
  • 28
  • 37