Nhu Trinh
Nhu Trinh

Reputation: 13986

Weird scala tuple behavior

I've notice this behavior in Scala

val list = List[(Int, Int)]()
val set = HashSet[(Int, Int)]()

scala> list :+ (1, 2)
res30: List[(Int, Int)] = List((1,2))

scala> list :+ (1 -> 2)
res31: List[(Int, Int)] = List((1,2))

scala> list :+ 1 -> 2
res32: List[(Int, Int)] = List((1,2))

//Work
// But the same for set not work
set += (1, 2)
<console>:14: error: type mismatch;
 found   : Int(2)
 required: (Int, Int)
       set += (1, 2)

//Ok may be += in set mean add all that mean this should work
set += ((1, 2))
set += ((1, 2), (3,4))
// Worked

// But why this one work
set += 1 -> 2
set += (1 -> 2)
set += ((1 -> 2))

Now I'm confuse, could you explain why tuple is not tuple?

scala> (4->5).getClass
res28: Class[_ <: (Int, Int)] = class scala.Tuple2

scala> (4,7).getClass
res29: Class[_ <: (Int, Int)] = class scala.Tuple2$mcII$sp

Upvotes: 3

Views: 61

Answers (2)

gandaliter
gandaliter

Reputation: 10111

I think the difference is that HashSet[T] defines two overloads for +=, one of which takes a single T, and the other takes multiple (as a T* params list). This is inherited from Growable[T], and shown here.

List[T].:+ can only take one T on the right hand side, which is why the compiler works out that it's looking at a tuple, not something that should be turned into a params list.

If you do set += ((1, 2)) then it compiles. Also, val tuple = (1,2); set += x works too.

See Mario Galic’s answer for why in the case of HashSet[T].+= the compiler chooses the overload that can't type over the one that can.

Upvotes: 5

Mario Galic
Mario Galic

Reputation: 48430

The parser stage -Xprint:parser gives

set.$plus$eq(1, 2)

which seems to resolve to

def += (elem1: A, elem2: A, elems: A*)

that is a method that accepts multiple arguments so compiler probably thinks elem1 = 1 or elem2 = 2 instead of considering (1,2) as a tuple.

missingfaktor points to SLS 6.12.3 Infix Operations as the explanation

The right-hand operand of a left-associative operator may consist of several arguments enclosed in parentheses, e.g. 𝑒;op;(𝑒1,…,𝑒𝑛). This expression is then interpreted as 𝑒.op(𝑒1,…,𝑒𝑛).

Now the operator += is left-associative because it does not end in :, and the right-hand operand of += consists of several arguments enclosed in parentheses (1,2). Therefore, by design, the compiler does not treat (1,2) as Tuple2.

Upvotes: 5

Related Questions