scala variable arguments :_* - scala

could someone bring more shed on following piece of scala code which is not fully clear to me? I have following function defined
def ids(ids: String*) = {
_builder.ids(ids: _*)
this
}
Then I am trying to pass variable argument list to this function as follows:
def searchIds(kind: KindOfThing, adIds:String*) = {
...
ids(adIds)
}
Firstly, ids(adIds) piece doesn't work which is a bit odd at first as error message says: Type mismatch, expected: String, actual: Seq[String]. This means that variable arguments lists are not typed as collections or sequences.
In order to fix this use the trick ids(adIds: _*).
I am not 100% sure how :_* works, could someone put some shed on it?
If I remember correctly : means that operation is applied to right argument instead to left, _ means "apply" to passed element, ...
I checked String and Sequence scaladoc but wasn't able to find :_* method.
Could someone explain this?
Thx

You should look at your method definitions:
def ids(ids: String*)
Here you're saying that this method takes a variable number of strings, eg:
def ids(id1: String, id2: String, id3: String, ...)
Then the second method:
def searchIds(kind: KindOfThing, adIds:String*)
This also takes a variable number of string, which are packaged into a Seq[String], so adIds is actually a Seq, but your first method ids doesn't take a Seq, it takes N strings, that's why ids(adIds: _*) works.
: _* this is called the splat operator, what that's doing is splatting the Seq into N strings.

Related

Scala passing varargs to another function that takes varargs

Why varargs can't be passed as another varargs without :_* ?
object Main {
def main(s: Array[String]): Unit = {
def someFunction(varars: String*) = {
someOtherFunction(varars) // Compilation ERRRO
someOtherFunction(varars:_*) // Works, but why ?
}
def someOtherFunction(someOtherVarars: String*): Unit = {
}
}
}
It's because varars is a single argument - an array of strings (note, that I'm not writing Array[String], because it is not the java Array, more details here), whereas by looking at the signature def someOtherFunction(someOtherVarars: String*): Unit, we can tell, that someOtherFunction takes multiple arguments of type String each. You cannot simply pass an array as the parameter to someOtherFunction, you need to "unfold" it first.
In other words an argument can be passed to someOtherFunction it has to be marked as a sequence argument. It would not make much sense to be able to pass varargs and varargs(1) to the single function. It's described in SLS ยง4.6.2.
"varargs"parameter means it can take any number of strings as an argument(i.e., a varargs field).
def someFunction(varars: String*): Seq[String] = {
varars }
if you define the above method and check for type of "varars" it has now become Seq[String] when you are using it. But when you pass it to another method which is expecting variableArgs type. It mismatch as it has become Seq[String] which should be converted to variable arguments using (someOtherVarars: _*).
_* operator tells the compiler to pass each element of the sequence as a separate argument, instead of passing it as a single argument.

Trying to skip implicit parameter list

I'd like to call a function returned by a function with an implicit parameter, simply and elegantly. This doesn't work:
def resolveA(implicit a: A): String => String = { prefix =>
s"$prefix a=$a"
}
case class A(n: Int)
implicit val a = A(1)
println(resolveA("-->")) // won't compile
I've figured out what's going on: Scala sees the ("-->") and thinks it's an attempt to explicitly fill in the implicit parameter list. I want to pass that as the prefix argument, but Scala sees it as the a argument.
I've tried some alternatives, like putting an empty parameter list () before the implicit one, but so far I've always been stopped by the fact that Scala thinks the argument to the returned function is an attempt to fill in the implicit parameter list of resolveA.
What's a nice way to do what I'm trying to do here, even if it's not as nice as the syntax I tried above?
Another option would be to use the apply method of the String => String function returned by resolveA. This way the compiler won't confuse the parameter lists, and is a little shorter than writing implicltly[A].
scala> resolveA[A].apply("-->")
res3: String = --> a=A(1)

Identical type mismatch in Scala

I have the snippet below from a code where rdd is RDD[(String,Vector)], but unfortunately my Scala compiler is complaining with the error Type mismatch, expected: RDD[(String,Vector)], actual: RDD[(String,Vector)] where I call flagVectorOutlier(rdd, predictedRDD)
def someFunction() {
testData.foreachRDD( rdd => {
val vectorsRDD = rdd.map( pair => pair._2 )
val predictedRDD = model.latestModel().predict( vectorsRDD )
flagVectorOutlier( rdd, predictedRDD )
} )
ssc.start()
ssc.awaitTermination()
}
def flagVectorOutlier(testVectors: RDD[(String, Vector)], predicts: RDD[Int]): Unit = {
}
Considering the actual and expected types are the same, what is the wrong point here? How could I solve this issue?
I've got this kind of error before.
It happened when I was using a java library which would use java.util.List, while my own code was using scala.collection.immutable.List and I mixed them up.
You can have two classes with the same name coexist in code easily, but the error message will not display fully-qualified names, so I would get Type mismatch, expected: List[Integer], actual: List[Integer] which seems puzzling. The solution is just to fully qualify the types of your input parameters or use typeDefs to distinguish them.
Bonus: I've also had a similar problem with tuples. For instance, when a method can either expect tuples or direct params:
def f(param: (Integer, String)) versus def f(param1: Integer, param2: String)
When calling the method with the wrong parameters (say, 2 params instead of a tuple), on a cursory inspection the error message can seem to show two identical types. This gets worse if you have several nested tuples (thus too many parentheses but the same types).

Varargs with different type parameters in scala

I'm new to Scala...
Anyway, I want to do something like:
val bar = new Foo("a" -> List[Int](1), "b" -> List[String]("2"), ...)
bar("a") // gives List[Int] containing 1
bar("b") // gives List[String] containing "2"
The problem when I do:
class Foo(pairs: (String, List[_])*) {
def apply(name: String): List[_] = pairs.toMap(name)
}
pairs is gonna be Array[(String, List[Any]) (or something like that) and apply() is wrong anyway since List[_] is one type instead of "different types". Even if the varargs * returned a tuple I'm still not sure how I'd go about getting bar("a") to return a List[OriginalTypePassedIn]. So is there actually a way of doing this? Scala seems pretty flexible so it feels like there should be some advanced way of doing this.
No.
That's just the nature of static type systems: a method has a fixed return type. It cannot depend on the values of the method's parameters, because the parameters are not known at compile time. Suppose you have bar, which is an instance of Foo, and you don't know anything about how it was instantiated. You call bar("a"). You will get back an instance of the correct type, but since that type isn't determined until runtime, there's no way for a compiler to know it.
Scala does, however, give you a convenient syntax for subtyping Foo:
object bar extends Foo {
val a = List[Int](1)
val b = List[String]("2")
}
This can't be done. Consider this:
val key = readStringFromUser();
val value = bar(key);
what would be the type of value? It would depend on what the user has input. But types are static, they're determined and used at compile time.
So you'll either have to use a fixed number of arguments for which you know their types at compile time, or use a generic vararg and do type casts during runtime.

Omitting dots when chaining calls

I don't understand why the following code doesn't compile:
class Abc
{
def b (x : String) = x + "abc"
def a (y : String) =
{
val ls : List[String] = y.lines toList
b (ls.head)
}
}
Main.scala:8: error: type mismatch;
found : java.lang.String
required: Int
b (ls.head)
When I change "y.lines toList" to
y.lines.toList
or even to
y.lines toList;
it does compile.
Perhaps the compiler understands it like
(y.lines).toList(b (ls.head))
or something like that, but I still don't understand the rules.
It's not obvious, and it's a combination of Scala's shortcut syntax and list indexing. If you want a hint, try redefining b to:
def b(x : String) = 0
You'll get some other compiler garbage back, but the error will change. In short, the Scala compiler will let you omit parens and dots for zero- or one-parameter methods, and we know b looks like it's somehow getting chained.. The rub is that Scala also uses parens for list indexing, so toList, which returns an iterator, may take one parameter as the list index. I'm not sure of this part exactly, but it looks like once you start omitting dots, the lexer will become greedy, and when it comes across a method that may take one parameter, will attempt to pass the next statement to it. In this case, that's a string, so it throws a syntax error.
You got it spot on with this:
(y.lines).toList(b (ls.head))
With the only possible correction being:
(y.lines).toList(b).apply(ls.head)
I'm not sure that Scala would decide in this particular case.
The rule, roughly speaking, is object (method parameters)* [method]. The compiler will continue as long as it finds tokens for a valid expression. A ; finishes the expression, and so would a ) or }. If the next line is blank, the expression also ends. If the next line begins with a reserved keyword (val, def, if, etc), the expression would end too.