Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala parenthesis semantics

Tags:

scala

When appending values to a MAP, why does Scala require the additional parenthesis-block to make this statement work?

Does NOT compile:

vmap += (item.getName(), item.getString()) // compiler output: "found: String"

However, this Does compile:

vmap += ((item.getName(), item.getString())) // note the second set of enclosures

TIA

EDIT: vmap is defined as

val vmap = new mutable.HashMap[String, String]()

Epilogue: At the time of this edit there are posts detailing two possible explanations, both of which appear to contain elements of truth to them. Which one is actually Correct? I couldn't say with any degree of certainty...I'm just a guy who's still learning the language. That being said, I have changed the answer selection based upon the feeling that the one answer is (at least to some extent) encompassed within the other - so I've opted for the larger picture, as I think it will provide a broader meaning for someone else in search of an answer. Ironically, I was trying to get a better understanding of how to flatten out some of the little nuances of the language, and what I've come to realize is there are more of those than I had suspected. I'm not saying that's a bad thing - in fact (IMO) it's to be expected from any language that is as flexible and complex - but it sure does make a guy miss the black/white world of Assembly from time-to-time...

To draw this to an end, a couple observations:
1) the selected answer contains a link to a website full of Scala brain-benders (Which I found extremely helpful in trying to understand some of the aforementioned quarks in the language.) Highly recommended.
2) I did come across another interesting twist - whereas the single-parenthesis (example above) does not work, change it the following and it works just fine...

vmap += ("foo" -> "bar")

Which probably has something to do with matching method/function signatures, but that is just a guess on my part.

like image 221
mjk Avatar asked Dec 20 '22 01:12

mjk


1 Answers

The accepted answer is actually wrong.

The reason you don't get tupling for Map.+= is that the method is overloaded with a second method that takes two or more args.

The compiler will only try tupling if the number of args is wrong. But if you give it two args, and there's a method that takes two, that's what it chooses, even if it fails to type check.

It doesn't start trying all possible combinations until something works because that would be fraught with obscurity. (Cf implicits.)

scala> def f(p: Pair[String, Int]) = { val (s,i) = p; s.toInt + i }
f: (p: Pair[String,Int])Int

scala> f("2",3)  // ok to tuple
res0: Int = 5

scala> trait F { def +=(p: Pair[String, Int]) = f(p) }
defined trait F

scala> val foo = new F {}
foo: F = $anon$1@6bc77f62

scala> foo += ("2",3)  // ok to tuple
res1: Int = 5

scala> trait G { def +=(p: Pair[String, Int]) = f(p); def +=(p:(String,Int),q:(String,Int),r:(String,Int)*) = f(p)+f(q)+(r map f).sum }
defined trait G

scala> val goo = new G {}
goo: G = $anon$1@183aeac3

scala> goo += ("2",3)    // sorry
<console>:12: error: type mismatch;
 found   : String("2")
 required: (String, Int)
              goo += ("2",3)
                      ^

scala> goo += (("2",3),("4",5),("6",7))
res3: Int = 27

I'd be remiss not to mention your friend and mine, -Xlint, which will warn about untoward arg adaptations:

apm@mara:~/tmp$ skala -Xlint
Welcome to Scala version 2.11.0-20130811-132927-95a4d6e987 (OpenJDK 64-Bit Server VM, Java 1.7.0_25).
Type in expressions to have them evaluated.
Type :help for more information.

scala> def f(p: Pair[String, Int]) = { val (s,i) = p; s.toInt + i }
f: (p: Pair[String,Int])Int

scala> f("2",3)
<console>:9: warning: Adapting argument list by creating a 2-tuple: this may not be what you want.
        signature: f(p: Pair[String,Int]): Int
  given arguments: "2", 3
 after adaptation: f(("2", 3): (String, Int))
              f("2",3)
               ^
res0: Int = 5

scala> List(1).toSet()
<console>:8: warning: Adapting argument list by inserting (): this is unlikely to be what you want.
        signature: GenSetLike.apply(elem: A): Boolean
  given arguments: <none>
 after adaptation: GenSetLike((): Unit)
              List(1).toSet()
                           ^
res3: Boolean = false

On the perils of adaptation, see the Adaptive Reasoning puzzler and this new one that is pretty common because we learn that the presence or absence of parens is largely a matter of style, and when parens really matter, using them wrong results in a type error.

Adapting a tuple in the presence of overloading:

scala> class Foo {
     | def f[A](a: A) = 1    // A can be (Int,Int,Int)
     | def f[A](a: A, a2: A) = 2
     | }
defined class Foo

scala> val foo = new Foo
foo: Foo = Foo@2645d22d

scala> foo.f(0,0,0)
<console>:10: warning: Adapting argument list by creating a 3-tuple: this may not be what you want.
        signature: Foo.f[A](a: A): Int
  given arguments: 0, 0, 0
 after adaptation: Foo.f((0, 0, 0): (Int, Int, Int))
              foo.f(0,0,0)
                   ^
res9: Int = 1
like image 106
som-snytt Avatar answered Jan 07 '23 01:01

som-snytt