/ / Місцеве призначення впливає на тип? - scala, generics

Місцевий присвоєння впливає на тип? - скала, генерики

У наступному прикладі f3 може прийняти Iterable [Array [Int]]

  def f3(o:Iterable[Iterable[Any]]):Unit = {}

f3(Iterable(Array(123)))    // OK. Takes Iterable[Array[Int]]

але якщо я призначу Iterable [масив [Int]] локальній змінній, він не може:

  val v3 = Iterable(Array(123))
f3(v3)    // Fails to take Takes Iterable[Array[Int]]

з помилкою:

  Error:(918, 10) type mismatch;
found   : Iterable[Array[Int]]
required: Iterable[Iterable[Any]]
f3(x)

Що за нісенітниця? Чому перший приклад працює, а не секунди. Здається, це має щось спільне з вкладеними дженериками:

  def f(o:Iterable[Any]):Unit = {}
f( Array(123))
val v1 = Array(123)
f(v1)  // OK

def f2(o:Iterable[Any]):Unit = {}
f2( Iterable(Array(123)))
val v2 = Array(123)
f(v2) // OK

З шкалою.2.11

Відповіді:

6 за відповідь № 1

Перш за все, це важливо Array не поширюється Iterable (тому що це тип Java). Натомість відбувається неявна конверсія з Array[A] до Iterable[A], тому очікувані типи мають значення.

У першому випадку: Iterable(Array(123)) є аргументом до f3 і, таким чином, перевіряється тип очікуваного типу Iterable[Iterable[Any]]. Так Array(123) перевіряється очікуваним типом Iterable[Any]. Ну, його фактичний тип Array[Int] і компілятор вставляє перетворення (тому що Iterable[Int] відповідає Iterable[Any]). Так це насправді Iterable(array2iterable(Array(123)) (Я не пам'ятаю точну назву).

У другому випадку f3 має тип Iterable[Array[Int]]: нічого не може викликати неявну конверсію в val f3 = ... лінія, правда? І немає неявного перетворення з Iterable[Array[Int]] до Iterable[Iterable[Int]] (або, загалом, від Iterable[A] до Iterable[B] коли є неявна конверсія з A до B), тому наступний рядок не вдалося скласти. Ви можете написати це перетворення самостійно, але це не допоможе, наприклад, перетворити Array[Array[Int]] до Iterable[Iterable[Int]].

І звичайно, якщо ви користуєтеся Iterable[Any], знову немає нічого, що може викликати неявну конверсію!


4 для відповіді № 2

Це пов'язано з тим, як працює висновок / об'єднання типу Scala.

Коли ви визначаєте змінну і виключаєте тип, Scala застосовує найбільш можливий тип:

scala> val v1 = Iterable(Array(123))
v1: Iterable[Array[Int]] = List(Array(123))

Однак, коли ви вказуєте очікуваний тип (наприклад, передаючи значення функції з визначеним типом параметра), Scala уніфікує даний параметр із очікуваним типом (якщо можливо):

scala> val v2 : Iterable[Iterable[Any]] = Iterable(Array(123))
v2: Iterable[Iterable[Any]] = List(WrappedArray(123))

З тих пір Int є підтипом Any, відбувається об'єднання, і код працює просто чудово.

Якщо ви хочете, щоб ваша функція приймала все, що є підтипом Any (без допомоги щодо об'єднання Скали) вам доведеться чітко визначити цю поведінку.

Редагувати:

Хоча те, що я кажу, частково правдиве, дивіться відповідь @AlexyRomanov для більш правильної оцінки. Схоже, що "об'єднання" між Array і Iterable насправді неявна конверсія, яка викликається при переході Iterable(Array(123)) як параметр (див. ефект цього в моїй декларації від v2)

Припустимо, у вас є трохи коду, де компілятор очікує типу B але знаходить тип A замість цього. Перш ніж видавати помилку, компілятор перевіряє набір неявних функцій перетворення для однієї з типом A => B. Якщо компілятор знайде задовільну конверсію, перетворення застосовується автоматично (і безшумно).

Причина f3 не подобається v1 це тому, що занадто пізно називати неявне перетворення на внутрішній Array[Int] і не існує жодної наявної неявної конверсії для Iterable[Array[Int]] => Iterable[Iterable[Int]], хоча це було б тривіально, як я показую нижче:

scala> implicit def ItAr2ItIt[T](ItAr: Iterable[Array[T]]): Iterable[Iterable[T]] = ItAr.map(_.toIterable)
ItAr2ItIt: [T](ItAr: Iterable[Array[T]])Iterable[Iterable[T]]

scala> def f3(o:Iterable[Iterable[Any]]):Unit = println("I like what I see!")
f3: (o: Iterable[Iterable[Any]])Unit

scala> val v3 = Iterable(Array(123))
v3: Iterable[Array[Int]] = List(Array(123))

scala> f3(v3)
I like what I see!