Syntax sugar: _* for treating Seq as method parameters - scala

I just noticed this construct somewhere on web:
val list = List(someCollection: _*)
What does _* mean? Is this a syntax sugar for some method call? What constraints should my custom class satisfy so that it can take advantage of this syntax sugar?

Generally, the : notation is used for type ascription, forcing the compiler to see a value as some particular type. This is not quite the same as casting.
val b = 1 : Byte
val f = 1 : Float
val d = 1 : Double
In this case, you're ascribing the special varargs type. This mirrors the asterisk notation used for declaring a varargs parameter and can be used on a variable of any type that subclasses Seq[T]:
def f(args: String*) = ... //varargs parameter, use as an Array[String]
val list = List("a", "b", "c")
f(list : _*)

That's scala syntax for exploding an array. Some functions take a variable number of arguments and to pass in an array you need to append : _* to the array argument.

Variable (number of) Arguments are defined using *.
For example,
def wordcount(words: String*) = println(words.size)
wordcount expects a string as parameter,
scala> wordcount("I")
1
but accepts more Strings as its input parameter (_* is needed for Type Ascription)
scala> val wordList = List("I", "love", "Scala")
scala> wordcount(wordList: _*)
3

Related

(Scala) How to convert List into a Seq

I have a function like
def myFunction(i:Int*) = i.map(a => println(a))
but I have a List of Int's.
val myList:List[Int] = List(1,2,3,4)
Desired output:
1
2
3
4
How can I programmatically convert myList so it can be inserted into myFunction?
Looking at your desired input and output, you want to pass a List where a varargs argument is expected.
A varargs method can receive zero or more arguments of same type.
The varargs parameter has type of Array[T] actually.
But you can pass any Seq to it, by using "varargs ascription/expansion" (IDK if there is an official name for this):
myFunction(myList: _*)

Why doesn't the scala compiler recognize this as a tuple?

If I create a map:
val m = Map((4, 3))
And try to add a new key value pair:
val m_prime = m + (1, 5)
I get:
error: type mismatch;
found : Int(1)
required: (Int, ?)
val m_prime = m + (1, 5)
If I do:
val m_prime = m + ((1, 5))
Or:
val m_prime = m + (1 -> 5)
Then it works. Why doesn't the compiler accept the first example?
I am using 2.10.2
This is indeed very annoying (I run into this frequently). First of all, the + method comes from a general collection trait, taking only one argument—the collection's element type. Map's element type is the pair (A, B). However, Scala interprets the parentheses here as method call parentheses, not a tuple constructor. The explanation is shown in the next section.
To solve this, you can either avoid tuple syntax and use the arrow association key -> value instead, or use double parentheses, or use method updated which is specific to Map. updated does the same as + but takes key and value as separate arguments:
val m_prime = m updated (1, 5)
Still it is unclear why Scala fails here, as in general infix syntax should work and not expect parentheses. It appears that this particular case is broken because of a method overloading: There is a second + method that takes a variable number of tuple arguments.
Demonstration:
trait Foo {
def +(tup: (Int, Int)): Foo
}
def test1(f: Foo) = f + (1, 2) // yes, it works!
trait Baz extends Foo {
def +(tups: (Int, Int)*): Foo // overloaded
}
def test2(b: Baz) = b + (1, 2) // boom. we broke it.
My interpretation is that with the vararg version added, there is now an ambiguity: Is (a, b) a Tuple2 or a list of two arguments a and b (even if a and b are not of type Tuple2, perhaps the compiler would start looking for an implicit conversion). The only way to resolve the ambiguity is to use either of the three approaches described above.

What is :_* and How it implement in Scala?

What i know:
scala> def fx(s: String *) = s.foreach(println)
fx: (s: String*)Unit
scala> val lst = List("1","2","3")
lst: List[java.lang.String] = List(1, 2, 3)
scala> fx(lst:_*)
1
2
3
What i want to know:
How can I implement :_*? by map?
Is there any other way that replace it?
How :_* defined in Scala?
Thank you
It is only a syntactic sugar to indicates to the compiler that you already provide a sequence of elements, there is no other "implementation" of it. For more information, you can refer to the Scala Language Specification (§6.6, p. 78)
For a method that takes variable arguments :_* means you want to pass the members of a collection as the variable arguments.
The corresponding varargs example to yours above:
scala> fx("a", "b", "c")
a
b
c
You can't implement -- it is a language feature. It doesn't mean anything outside the context of calling a varargs method.

What does param: _* mean in Scala?

Being new to Scala (2.9.1), I have a List[Event] and would like to copy it into a Queue[Event], but the following Syntax yields a Queue[List[Event]] instead:
val eventQueue = Queue(events)
For some reason, the following works:
val eventQueue = Queue(events : _*)
But I would like to understand what it does, and why it works? I already looked at the signature of the Queue.apply function:
def apply[A](elems: A*)
And I understand why the first attempt doesn't work, but what's the meaning of the second one? What is :, and _* in this case, and why doesn't the apply function just take an Iterable[A] ?
a: A is type ascription; see What is the purpose of type ascriptions in Scala?
: _* is a special instance of type ascription which tells the compiler to treat a single argument of a sequence type as a variable argument sequence, i.e. varargs.
It is completely valid to create a Queue using Queue.apply that has a single element which is a sequence or iterable, so this is exactly what happens when you give a single Iterable[A].
This is a special notation that tells the compiler to pass each element as its own argument, rather than all of it as a single argument. See here.
It is a type annotation that indicates a sequence argument and is mentioned as an "exception" to the general rule in section 4.6.2 of the language spec, "Repeated Parameters".
It is useful when a function takes a variable number of arguments, e.g. a function such as def sum(args: Int*), which can be invoked as sum(1), sum(1,2) etc. If you have a list such as xs = List(1,2,3), you can't pass xs itself, because it is a List rather than an Int, but you can pass its elements using sum(xs: _*).
For Python folks:
Scala's _* operator is more or less the equivalent of Python's *-operator.
Example
Converting the scala example from the link provided by Luigi Plinge:
def echo(args: String*) =
for (arg <- args) println(arg)
val arr = Array("What's", "up", "doc?")
echo(arr: _*)
to Python would look like:
def echo(*args):
for arg in args:
print "%s" % arg
arr = ["What's", "up", "doc?"]
echo(*arr)
and both give the following output:
What's
up
doc?
The Difference: unpacking positional parameters
While Python's *-operator can also deal with unpacking of positional parameters/parameters for fixed-arity functions:
def multiply (x, y):
return x * y
operands = (2, 4)
multiply(*operands)
8
Doing the same with Scala:
def multiply(x:Int, y:Int) = {
x * y;
}
val operands = (2, 4)
multiply (operands : _*)
will fail:
not enough arguments for method multiply: (x: Int, y: Int)Int.
Unspecified value parameter y.
But it is possible to achieve the same with scala:
def multiply(x:Int, y:Int) = {
x*y;
}
val operands = (2, 4)
multiply _ tupled operands
According to Lorrin Nelson this is how it works:
The first part, f _, is the syntax for a partially applied function in which none of the arguments have been specified. This works as a mechanism to get a hold of the function object. tupled returns a new function which of arity-1 that takes a single arity-n tuple.
Futher reading:
stackoverflow.com - scala tuple unpacking

How to invoke a method taking String * with elements of Array[String]

Suppose I have a method
def f(s:String *) = s.foreach( x => println(x) )
Now I have an array:
val a = Array("1", "2", "3")
How do I invoke f with elements of a as parameters?
EDIT:
So given a, I want to do the following:
f(a(0), a(1), a(2)) // f("1", "2", "3")
There is an operator for that:
f(a: _*)
This operation is defined in chapter 4.6.2 Repeated Parameters of The Scala Language Specification Version 2.9 and further explained in 6.6 Function Applications:
The last argument in an application may be marked as a sequence argument, e.g.
e : _*. Such an argument must correspond to a repeated parameter (§4.6.2) of type
S * [...]. Furthermore, the type of e must conform to scala.Seq[T], for some type T which conforms to S. In this
case, the argument list is transformed by replacing the sequence e with its elements.
BTW your f function can be simplified:
def f(s:String *) = s foreach println
Or better (equals sign is discouraged as it suggests that the method actually returns something, however it "only" return Unit):
def f(s:String *) {s foreach println}