2013-08-14 2 views
3

При добавлении значений в MAP, почему Scala требует дополнительный блок скобок, чтобы заставить этот оператор работать?Семантика семантики Scala

не компилируется:

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

Однако это скомпилировать:

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

ТИА

EDIT: VMAP определяется как

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

Эпилог: Во время этого редактирования есть сообщения, в которых подробно изложены два возможных объяснения, оба из которых, как представляется, содержат в себе элементы истины. Какой из них действительно правильный? Я не мог сказать с какой-либо степенью уверенности ... Я всего лишь парень, который все еще изучает язык. При этом я изменил выбор ответа на основании чувства, что один ответ (по крайней мере, до некоторой степени) охвачен внутри другого - так что я выбрал большую картинку, так как я думаю, что это обеспечит более широкий смысл для кого-то другого в поисках ответа. По иронии судьбы, я пытался лучше понять, как смягчить некоторые тонкие нюансы языка, и то, что я понял, есть больше, чем я подозревал. Я не говорю, что это плохо - ведь (IMO) можно ожидать от любого языка, который является столь же гибким и сложным, но он уверен, что парень пропустит черно-белый мир Ассамблеи от времени до -time ...

Чтобы нарисовать это до конца, несколько наблюдений:
1) выбранный ответ содержит ссылку на сайт, полный изгибов мозга Scala (который я нашел чрезвычайно полезным в попытке понять некоторые вышеупомянутых кварков на языке.) Очень рекомендуется.
2) я наткнулся еще один интересный поворот - в то время как одной скобкой (пример выше) не работает, замените его на следующий, и он прекрасно работает ...

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

Который, вероятно, имеет что-то делать с подписями метода/функции, но это только догадка с моей стороны.

ответ

7

принятый ответ на самом деле ошибочен.

Причина, по которой вы не получаете tupling для Карты. + = - это то, что метод перегружен вторым методом, который принимает два или более аргумента.

Компилятор будет пытаться только повторить попытку, если число аргументов неверно. Но если вы дадите ему два аргумента, и есть метод, который принимает два, это то, что он выбирает, даже если он не может ввести проверку.

Он не начинает пытаться использовать все возможные комбинации до тех пор, пока что-то не будет работать, потому что это будет чревато безвестностью. (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 = [email protected] 

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 = [email protected] 

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 

Я бы упущением не упомянуть своего друга и мой, -Xlint, который будет предупреждать о неблагоприятных ARG адаптации:

[email protected]:~/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 

Об опасностях адаптации см the Adaptive Reasoning puzzler и this new one that is pretty common, потому что мы узнаем, что присутствие или отсутствие парнеров в значительной степени зависит от стиля, и когда парны действительно имеют значение, используя их неправильные результаты в ошибке типа.

Приспосабливая кортеж в присутствии перегрузки:

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 = [email protected] 

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 
+0

Чтобы убедиться, что я понимаю сердце материи (имея в виду, что я до сих пор относительно новичок в Scala) ... Я думаю, что вы показываете с f (и foo), так это потому, что они не имеют множественного метода подписи (ig, перегруженные), компилятор имеет широту (и будет) выводить кортеж при использовании только одной круглой скобки. Однако, поскольку g (и, следовательно, goo) имеет несколько сигнатур (и, следовательно, двусмысленность в отношении наилучшего соответствия), компилятор откажется от возможности ложного вывода - и, следовательно, требуется вторая скобка, поскольку она эффективно действует как литерал-кортеж. Это точно? – mjk

+0

Почти. Вывод кортежа называется адаптированием arg, см. Редактирование, показывающее, что -Xlint скажет вам, когда это произойдет. Перед этим разрешается перегрузка.В этом случае существует только один метод, который может быть применен к двум аргументам, поэтому нет никакой двусмысленности как таковой (кроме наших голов). Люди называют это «Злой перегрузкой». Не все перегрузки создаются равными; это очень мягко. Некоторые плохо спроектированные перегрузки (или унаследованные от Java) заставляют вас сказать, я попросил f1 и получил f2, что должно быть ошибкой компилятора. –

+0

Добавлен пример tupling, где требуется привязка для приложения, немного сложная, так как вывод принимает A как Tuple3. –

3

Поскольку метод Map += принимает параметр Tuple2[A, B].

Вы должны пометить Tuple2[A, B] окружающими круглыми скобками, иначе компилятор не выберет тип Tuple2.

Tuple2 простая пара А -> В.

val x = (5, 7); // the type is inferred to Tuple2[Int, Int]; 

Map итерации делает это еще более очевидным:

map.foreach { case (key, value) => ...}; 

(key, value)// is a Tuple2 

Первый набор вмещающих скобки рассматриваются как бесплодными/пропустить возможно, компилятором. Второй набор круглых скобок создает необходимые Tuple2.

val x = (item.getName(), item.getString());//Tuple2[String, String] 
vmap += x; // THIS COMPILES 

vmap += (item.getName(), item.getString())// is identical to 
vmap += item.getName(), item.getString() // now it thinks you are adding a String, the result of item.getName() 

vmap += ( (item.getName(), item.getString()) )// skips the first set, sees the Tuple, compiles. 

От СЛС:

Постфиксные операторы имеют более низкий приоритет, чем операторы инфиксные, так Foo бар Баз = foo.bar (БАЗ)

В этом случае: vmap += (item.getName(), item.getString()); = vmap.+=(item.getName());

+0

я, вероятно, следовало бы более ясно в моем вопросе: что я особенно пытаюсь понять, почему ваш первый пример выводится в виде кортежей, в то время как как мой первый пример выведен как простая строка? – mjk

+0

Я знаю, что это педантичный вопрос, но тогда как/когда дифференцирует компилятор Scala? Например, основываясь на вашем примере, почему (5, 7) не требуется, чтобы вторая скобка была выведена как tuple2? – mjk

+0

Игнорировать комментарий - это было сделано до того, как я увидел ревизию вашего ответа. Спасибо, что нашли время уточнить. – mjk

Смежные вопросы