Каковы правила, когда параметры типа trait / class имеют приоритет перед параметрами типа метода - PullRequest
2 голосов
/ 31 марта 2012

Я уже некоторое время использую scala, и я подумал, что действительно начинаю понимать все (ну, большинство вещей ...), но я был озадачен рядом определений методов в классе Map. Я знаю, как работают foldLeft и т. Д., Но меня смущает то, что параметры типа используются в функциях Map. Давайте использовать в качестве примера foldLeft:

foldLeft [B] (z: B)(op: (B, (A, B)) ⇒ B) : B

Определение для признака самой карты принимает два параметра типа «A» и «B» (например, Map [A, + B]). Из того, что я могу сказать, когда вы определяете параметр типа для метода, используя то же имя, что и один из параметров типа для класса / признака, он переопределяет значение класса / признака. Возьмем это определение Foo в качестве примера:

class Foo[A : Manifest, B : Manifest] {
  def isAString() = manifest[A] == manifest[String]
  def isAInt() = manifest[A] == manifest[Int]
  def isBString() = manifest[B] == manifest[String]
  def isBInt() = manifest[B] == manifest[Int]
  def nowIsBString[B : Manifest] = manifest[B] == manifest[String]
}

scala> val f = new Foo[String,Int]
f: Foo[String,Int] = Foo@7bacb41

scala> f.isAString
res290: Boolean = true

scala> f.isAInt
res291: Boolean = false

scala> f.isBString
res292: Boolean = false

scala> f.isBInt
res293: Boolean = true

scala> f.nowIsBString[String]
res294: Boolean = true

scala> f.nowIsBString[Int]
res295: Boolean = false

Таким образом, в определении foldLeft «B» происходит из определения метода, а «A» - из определения признака. Например:

val xm = Map("test" -> 1, "test2" -> 2)

scala> val foldFn = (z: Int, kv: (String, Int)) => z + kv._2 
foldFn: (Int, (String, Int)) => Int = <function2>

scala> m.foldLeft(0)(foldFn)
res298: Int = 3

Это то же самое, что и 'B' для функции, совпадающей с 'B' для признака, но что если я поменяю тип 'B' для функции на String вместо Int:

scala> val foldFn = (z: String, kv: (String, String)) => z + kv._2 
foldFn: (String, (String, String)) => java.lang.String = <function2>

scala> m.foldLeft("")(foldFn)
<console>:19: error: type mismatch;
 found   : (String, (String, String)) => java.lang.String
 required: (java.lang.String, (java.lang.String, Int)) => java.lang.String
              m.foldLeft("")(foldFn)

Итак, давайте изменим параметр kv на (String, Int):

scala> val foldFn = (z: String, kv: (String, Int)) => z + kv._2 
foldFn: (String, (String, Int)) => java.lang.String = <function2>

scala> m.foldLeft("")(foldFn)
res299: java.lang.String = 12

В отличие от моего примера Foo, в этом случае значение «B» карты имеет приоритет над определением функций, но только для параметра kv. То, что я ожидал, это увидеть foldLeft, определенный следующим образом:

foldLeft[C] (z: C)(op: (C, (A, B)) => C): C

Это было бы более понятно для меня, но это не определено таким образом. Так кто-нибудь знает правила, когда параметр методов переопределяет параметр trait / class, а когда нет -

1 Ответ

1 голос
/ 31 марта 2012

Scala в этом отношении аналогичен Java, и из главы «1001» применима следующая спецификация Java:

Объявление d типа с именем n затеняет объявления любых других типов с именем n , которые находятся в области действия в точке, где d встречается во всей области действия d .

Таким образом, параметр типа для метода всегда будет скрывать параметр типа класса или свойства с тем же именем.Ваш пример Foo демонстрирует этот факт.

Очевидный контрпример, который вы видите в случае Map foldLeft, является просто неприятным артефактом текущей версииСкаладок, как указал в ответах на вопрос, который вы связали.foldLeft не определен в признаке Map, но в TraversableOnce, где вообще отсутствует параметр типа признака с именем B.

В общемКонечно, замаскировать параметр типа для свойства или класса в методе - действительно плохая идея.

...