What does the : mean after a value in Scala? - scala

I just started learning Scala, and I've noticed that : is used in many places. Most of the time, the : usage makes sense, e.g. after parameter names or method declarations. The following usage confuses me, however:
val a = Seq[String]("a", "b")
a :+ "c"
or
def myMethod(varargs: String*) {
// ...
}
val a = Seq[String]("a", "b", "c")
myMethod(a:_*)
What exactly is the : doing in these cases? Why can't I call a._* directly?

The two usages that you are asking about are two completely different cases.
a :+ "c"
The : doesn't mean anything by itself here; it's part of a method named :+, which appends an element to a Seq.
myMethod(a:_*)
Here, you have a method myMethod which takes a variable number of arguments. You want to use the Seq to fill the arguments; the : _* indicates that you want to do that (rather than pass the Seq itself as the first argument of the method).
Note that : has a special meaning if a method name ends with : (not if it begins with : as in your first example). In that case, the method will be right-associative; it means that the method will be called on the thing on the right, with the thing on the left as the argument, rather than the reverse.

It is telling the compiler that you want your sequence to be taken apart and supplied as separate parameters.
def myMethod(varargs: String*) = {
// varargs is a Seq
val x:Seq[String] = varargs
}
// Calling it is different
myMethod("a", "b")
// To call using a seq, you need to signal the compiler
myMethod(mySeqOfString: _*)
// They choose the `:` because this would be valid too:
myMethod(myString: String)
Int the other example you gave a :+ "c" the : has a different meaning. It's simply part of the method name. They could have named it append, but they chose :+. The reason is that the : in +: has a special meaning: bind to the right. This allows for "c" +: a. So for sake of consistency they probably chose :+ for append and +: for prepend.

:+ is a method on Seq[A] which appends the given item to the sequence.
In the second example, myMethod(a : _*) the a : _* is used to pass a sequence as the variable argument list to the myMethod function.

The significance of the colon in Scala varies according to context.
As you note, it separates an identifier in a declaration from its type.
It can also appear as part of an operator involving a sequence.
Since an operator in Scala is really just a method, its use here is arbitrary, but you can rely on the convention that the colon is on the side of a binary operator that points to the sequence.
So your example
a :+ "c"
Appends the string "c" to a sequence of strings, while
"c" +: a
Prepends the string to the sequence. (Or, more precisely in the case of an immutable sequence, returns a new sequence resulting from the prepend/append operation. See documentation)
Your final example
myMethod(a: _*)
(Which I had to look up thanks for teaching! see documentation) tells the compiler to expand the sequence as varargs (rather than as a single argument).

Related

Why is a scala Set casted to a Vector instead of a List?

I wonder why a Set[A] is converted to a Vector[A] if I ask for a Seq[A] subclass? To illustrate this take the following example:
val A = Set("one", "two")
val B = Set("one", "two", "three")
def f(one: Seq[String], other : Seq[String]) = {
one.intersect(other) match {
case head :: tail => head
case _ => "unknown"
}
}
f(A.to, B.to)
This function will return "unknown" instead of one. The reason is that A.to will be casted to a Vector[String]. The cons operator (::) is not defined for Vectors but for Lists so the second case is applied and "unknown" is returned. To fix this problem I could use the +: operator which is defined for all Seqs or convert the Set to List (A.to[List]). So my (academic) question is:
Why does A.to returns a Vector. At least according to the scala docs the default implementation of Seq is LinearSeq and the default of this is List. What did I got wrong?
Because it can, you are depending on runtime class implementation details, instead of compile-time type information guarantees. The to or toSeq method is free to return anything that typechecks, it could even generate a random number and chose a concrete class in base of that number, so you may get a List something other times a Vector or whatever. It may even decide in base of the operating system. Of course, I am being pedantic here and hopefully, they do not do that, but my point is, we can't really explain, that is what the implementation does and it may change in the future.
Also, the "default implementation of Seq is a List", applies only in the constructor. And again, they may change that in any moment.
So, if you want a List ask for a List, not for a Seq.

Why can't I call the Scala Seq.empty[Int]() method?

I'm trying to create a class with an attribute named _1d_matrix, which is a 1D matrix. I would want to fill it with 0.
Thus, I tried to call the method empty of the Seq object (of course not the trait), as mentionned in the documentation : http://www.scala-lang.org/api/2.12.3/scala/collection/Seq$.html#empty[A]:CC[A]
For the moment my code is :
class Calc(val width : Int) {
private val _1d_matrix : Seq.empty[Int]()
}
But there are three errors :
empty is undefined
() can't be written (2 errors : "; or newline is expected" + "definition or declaration is expected")
I think it's because it's not allowed to directly make use of Seq instead of List (e.g.). But : why ? After all, Seq is an object (and a trait but not in my code).
Right way to initialize field looks like this:
private val _1d_matrix = Seq.empty[Int]
or
private val _1d_matrix: Seq[Int] = Seq()
There are two ways to define a 0-arity (no arguments) method in scala - with a pair of parenthesis after the name def exit(), and without one: def empty.
When it is defined in the former way, you can call it with or without parenthesis: exit() or exit - both work. But when parenthesis are not included into the method definition, you can not have them at the call site either: Seq.empty[Int] works, but Seq.empty[Int]() does not.
The convention is to use parenthesis for functions that have side effects, and not use them for purely functional calls.
The other problem with your code is that you have a : where it needs to be an assignment.
So, something like this should work:
val _1d_matrix = Seq.empty[Int]
Your thinking is correct but you have a couple syntax errors.
private val _1d_matrix : Seq.empty[Int]()
: here is used to define a type annotation, so it's trying to find the type Seq.empty rather than the method. That fails because there is no such type.
Use = instead to assign a value. Adding the type here is optional since Scala is able to infer the correct type.
The other is that the empty method is defined without parens, so you must use Seq.empty[Int] instead of Seq.empty[Int]()

Scala Slick Bulk Insert with Array

I'm splitting a long string into an array of strings, then I want to insert all of them to the database. I can easily loop through the array and insert one by one, but it seems very inefficient. Then I think there is a insertAll() method. However, the insertAll() method is defined like this:
def insertAll(values: U*)
This only accepts multiple U, but not an Array or List.
/* Insert a new Tag */
def insert(insertTags: Array[String])(implicit s: Session) {
var insertSeq: List[Tag] = List()
for(tag <- insertTags) {
insertSeq ++= new Tag(None, tag)
}
Tag.insertAll(insertSeq)
}
*Tag is the table Object
This is the preliminary code I have written. It doesn't work because insertAll() doesn't take Seq. I hope there is a way to do this...so it won't generate an array length times SQL insertion clause.
When a function expect repeated parameters such as U* and you want to pass a sequence of U instead, it must be marked as a sequence argument, which is done with : _* as in
Tag.insertAll(insertSeq: _*)
This is described in the specification in section 6.6. It disambiguates some situations such as ;
def f(x: Any*)
f(Seq(1, "a"))
There, f could be called with a single argument, Seq(1, "a") or two, 1 and "a". It will be the former, the latter is done with f(Seq(1, "a"): _*). Python has a similar syntax.
Regarding the questions in your comment :
It works with Seq, collections that confoms to Seq, and values that can be implicitly converted to Seqs (which includes Arrays). This means a lot of collections, but not all of them. For instances, Set are not allowed (but they have a toSeq methods so it is still easy to call with a Set).
It is not a method, it it more like a type abscription. It simply tells the compiler that this the argument is the full expected Seq in itself, and not the only item in a sequence argument.

trouble understanding with Map and map in scala

I tried to make some sort of convenient class (below) to hold folder and get file by using filename (string). This work as expect but one thing I don't understand is map part Map(folder.listFiles map {file => file.getName -> file}:_*).
I place :_* there to prevent some kind of type incompatible but I don't know what does it really do. Also, what is _* and could I replace is with anything more specific ?
thanks
class FolderAsMap (val folderName:String){
val folder = new File(folderName)
private val filesAsMap: Map[String, File] = Map(folder.listFiles map
{file => file.getName -> file}:_*)
def get(fileName:String): Option[File] = {
filesAsMap.get(fileName)
}
}
: _* is correct. Alternatively, you can use toMap:
folder.listFiles map {file => file.getName -> file}.toMap
Map(...) is method apply in object Map: def apply [A, B] (elems: (A, B)*): Map[A, B]. It has a repeated parameter. It is expected to be called with multiple parameters. The : _* is used to signal you are passing all the parameters as just one Seq argument.
It avoids ambiguities. In java, (where equivalent varargs are arrays instead of Seqs) there is a possible ambiguity, if a method f(Object... args) and you call it with f(someArray), it could mean that args has just one item, with is someArray (so f receives an array of just one element, which its someArray), or args is someArray and f receives someArray directly). Java choose the second version. In scala, with a richer type system and Seq rather than Array the ambiguity may arise much more often, and the rule is that you always have to write : _* when passing all arguments as one, even when no ambiguity is possible, as in here, rather than a complex rule to tell when there is an actual ambiguity.
The _* makes the compiler pass each element of folder.listFiles map { file => file.getName -> file} as an own argument to Map instead of all of it as one argument.
In this case the map function creates a Array (because folder.listFiles returns that type). So if you write:
val files = folder.listFiles map { file => file.getName -> file }
...the returned type will be Array[(String, File)]. To convert this to a Map you will need to pass files one by one to the maps constructor using the _* (or use the method toMap like #didierd wrote):
val filesAsMap = Map(files : _*)

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.