Rahul Sharma Rahul Sharma - 8 months ago 57
Scala Question

What does dot colon colon (.::) meaning in scala

Below code adds element to

list. My question is how scala internally translates

Code snippet:

var res = List[(Int, Int)]()
res .::= (1, 2)
res .::= (3, 4)


res56: List[(Int, Int)] = List((1,2),(3,4))


There are a few things going on in that snippet. Before diving into it let's talk about the difference between var and val. Namely, that a variable declared using the val keyword is immutable, i.e. its value cannot be changed:

scala> val x = 1
x: Int = 1

scala> x = 2
<console>:13: error: reassignment to val
       x = 2

On the other hand, var keyword is used to declare a mutable variable, i.e. its value can be changed:

scala> var y = "bar"
y: String = bar

scala> y = "foo"
y: String = foo

What if we wanted to compute a new value of y by appending to its current value?

scala> y = y + "bar"
y: String = foobar

Sure that works, but it turns out there's a shorthand for doing that:

scala> y += "bar"

scala> y
res10: String = foobar

By the way, in Scala, + is just a name of a method, so y + "bar" is the same as y.+("bar"). Ugly, but valid. Similarly, y.+=("bar") is also a valid replacement of y += "bar".

Great, let's remember that for later. Next, as others have already pointed out, :: is just a method for prepending elements to a list (from Java it can be invoked as someList.$colon$colon(someElement)). The important thing to note is that the :: method returns a new list:

scala> var letters = List("b", "c")
letters: List[String] = List(b, c)

scala> letters.::("a")
res1: List[String] = List(a, b, c)

scala> letters
res2: List[String] = List(b, c)

What if we wanted to set letters to the list which contains the letter "a"?

scala> letters = letters.::("a")
letters: List[String] = List(a, b, c)

Notice that this looks awfully similar to the previous example with strings. Does the shorthand work here too?

scala> letters ::= "a"

scala> letters
res6: List[String] = List(a, b, c)

Yes, it does. letters.::=("a") works as well.

Now, let's break down the original snippet:

Step 1

Create a variable named res and assign it an empty, immutable list. This empty list is intended to contain pairs of integers (Int, Int).

var res = List[(Int, Int)]()

Here's an alternative way of doing the same thing:

var res = List.empty[(Int, Int)]

(which, in my opinion, is a bit easier to read)

Step 2

Prepend a new element (1, 2) to list res and reassign the resulting list back to res.

res .::= (1, 2)

Or, without spaces:

res.::=(1, 2)

Looks familiar? We could've also written it out as:

res = res.::(1, 2)

Step 3

Prepend (3, 4) following the logic in step 2

Step 4

Print out current value of res, which should be: List((3,4), (1,2))

Side note

Confusingly, the compiler is lenient enough to allow us to specify only a single set of parentheses when calling ::, though we really ought to have two sets: one for the method invocation and another one for indicating a pair of integers. So, there happens to be yet another valid way of writing the same thing res.::=((1, 2)).

More generally:

scala> def first(p:(Int, Int)):Int = p._1
first: (p: (Int, Int))Int

scala> first(6,7)
res0: Int = 6

scala> first((6,7))
res1: Int = 6

scala> first(6 -> 7) //lolz! another one using implicit conversion
res2: Int = 6

The implicit conversion is ever-present since it's defined in Predef.ArrowAssoc

mind = blown

I also recommend taking a look at What are all the instances of syntactic sugar in Scala?