Почему Scala предупреждает о стирании стилей в первом случае, но не о втором?
У меня есть две функции (они не редактировались с оригинала - некоторые из ответов ниже отвечают на оригинальные, которые возвращают последовательность()):
def foo1[A](ls: Iterable[A]) : Iterator[A] =
for (List(a, b) <- ls sliding 2) yield a
def foo2[A](ls: Iterable[A]) : Iterator[A] =
for (a::b::Nil <- ls sliding 2) yield a
которые я наивно думал, были одинаковыми. Но Scala дает это уменьшение только для первого:
warning: non variable type-argument A in type pattern List[A]
is unchecked since it is eliminated by erasure
Я думаю, что понимаю, почему он дает эту ошибку для первого: Scala думает, что я пытаюсь использовать этот тип как условие для шаблона, то есть совпадение с List[B](_, _)
должно завершиться неудачно, если B не наследует от A, за исключением того, что этого не может произойти, потому что тип стирается в обоих случаях.
Итак, два вопроса:
1) Почему второй не дает такого же предупреждения?
2) Можно ли убедить Scala, что тип действительно известен во время компиляции и, следовательно, не может не совпадать?
edit: Я думаю, отвечает на мой первый вопрос. Но меня все еще интересует второй.
edit: agilesteel упоминается в комментарии, что
for (List(a, b) <- List(1,2,3,4) sliding 2) yield ()
не выдает предупреждения. Как это отличается от foo1
(не следует ли удалять параметр [Int]
так же, как и параметр [A]
)?
Ответы
Ответ 1
Я не уверен, что здесь происходит, но статический тип Iterable[A].sliding
- это Iterator[Iterable[A]]
, а не Iterator[List[A]]
, который будет статическим типом List[A].sliding
.
Вы можете попробовать получить Seq
вместо Iterable
, и это тоже работает. РЕДАКТИРОВАТЬ Вопреки тому, что я ранее утверждал, оба Iterable
и Seq
являются со-вариантами, поэтому я не знаю, что другое. END EDIT Определение sliding
тоже довольно странно:
def sliding [B >: A] (size: Int): Iterator[Iterable[A]]
Посмотрите, как требуется B
, суперкласс A
, который никогда не используется? Контрастируйте это с помощью Iterator.sliding
, для которого нет проблем:
def sliding [B >: A] (size: Int, step: Int = 1): GroupedIterator[B]
Во всяком случае, во втором случае:
for (a::b::Nil <- ls sliding 2) yield a
Здесь вы разложите список дважды, и для каждого разложения тип head
проверяется на A
. Поскольку тип head
не стирается, у вас нет проблемы. Это также в основном предположение.
Наконец, если вы превратите ls
в List
, у вас не будет проблемы. Короче говоря, я не думаю, что ты можешь что-то сделать. В противном случае вы также можете написать следующее:
def foo1[A](ls: Iterable[A]) : Iterator[A] =
for (Seq(a, b) <- ls.iterator sliding 2) yield a
Ответ 2
1) Второй не вызывает предупреждения, вероятно, потому, что вы создаете список (или шаблон), добавляя элементы к объекту Nil
, который расширяет List
, параметризуя его с помощью Nothing
. А так как все Nothing
, не о чем беспокоиться;) Но я не уверен, действительно угадываю здесь.
2) Почему бы вам просто не использовать:
def foo[A](ls: Iterable[A]) =
for (list <- ls sliding 2) yield ()
Ответ 3
Я не очень хорошо знаком с Scala, но я использовал Java и Haskell, поэтому я собираюсь выйти на конечность здесь и предположить, что ваш второй пример не совсем то же самое. Похоже, что оба они используют совпадение шаблонов, чтобы деконструировать первые два элемента списка, но второй использует оператор cons в отличие от конструктора List. Я предполагаю, что это как-то связано с взаимодействием с Java и тонкой разницей между тем, как фактически работает конструктор List по сравнению с оператором cons.
С другой стороны, может быть, это просто край, который был упущен компилятором, и оба они должны сгенерировать предупреждение.