Подтвердить что ты не робот

Вывод типа не выполняется Set, сделанный с .toSet?

Почему здесь не возникает вывод типа?

scala> val xs = List(1, 2, 3, 3)
xs: List[Int] = List(1, 2, 3, 3)

scala> xs.toSet map(_*2)
<console>:9: error: missing parameter type for expanded function ((x$1) => x$1.$times(2))
       xs.toSet map(_*2)

Однако, если xs.toSet назначен, он компилируется.

scala> xs.toSet
res42: scala.collection.immutable.Set[Int] = Set(1, 2, 3)

scala> res42 map (_*2)
res43: scala.collection.immutable.Set[Int] = Set(2, 4, 6)

Также, идя в другую сторону, преобразование в Set из List, а соответствие на List соответствует.

scala> Set(5, 6, 7)
res44: scala.collection.immutable.Set[Int] = Set(5, 6, 7)

scala> res44.toList map(_*2)
res45: List[Int] = List(10, 12, 14)
4b9b3361

Ответ 1

Я согласен, что было бы неплохо вывести "единственный возможный" тип, даже если звонки закованы в цепочку, но есть технические ограничения.

Вы можете думать о выводе как о широком пробеле над выражением, собирая ограничения (которые возникают из ограничений подтипа и требуемых неявных аргументов) для переменных типа, а затем решая эти ограничения. Этот подход позволяет, например, имплицировать для указания вывода типа. В вашем примере, хотя существует единственное решение, если вы смотрите только на подвыражение xs.toSet, более поздние вызовы с цепочкой могут вводить ограничения, которые делают систему неудовлетворительной. Недостатком отказа от переменных типа, которые не были решены, является то, что вывод типа для замыканий требует, чтобы целевой тип был известен, и, таким образом, он терпит неудачу (ему нужно что-то конкретное для продолжения - требуемый тип замыкания и тип его типов аргументов должны не оба неизвестны).

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

Ответ 2

В: Почему toSet не делает то, что я хочу?

A: Это было бы слишком легко.

В: Но почему это не компилируется? List(1).toSet.map(x => ...)

A: Компилятор Scala не может сделать вывод, что x является Int.

Q: Что, это глупо?

A: Ну, List[A].toSet не возвращает immutable.Set[A]. Он возвращает immutable.Set[B] для неизвестного B >: A.

Q: Как я должен был знать это?

A: из Scaladoc.

Q: Но почему toSet определяется таким образом?

A: Вы можете предположить, что immutable.Set является ковариантным, но это не так. Он инвариантен. И возвращаемый тип toSet находится в ковариантной позиции, поэтому тип возврата не может быть инвариантным.

Q: Что значит "ковариантная позиция"?

A: Позвольте мне Википедию, что для вас: http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science). См. Также главу 19 Odersky, Venners and Spoon.

В: Теперь я понимаю. Но почему неизменен. Установить инвариант?

A: Позвольте мне стекать переполнение для вас: Почему Scala неизменный набор не ковариант в своем типе?

В: Я сдаюсь. Как исправить исходный код?

A: Это работает: List(1).toSet[Int].map(x => ...). Так делает это: List(1).toSet.map((x: Int) => ...)

(с извинениями перед Фридманом и Феллесином. спасибо to paulp и ijuma за помощь)

РЕДАКТИРОВАТЬ: В Adriaan есть ценная дополнительная информация и в обсуждении в комментариях как здесь, так и здесь.

Ответ 3

Вывод типа не работает должным образом, поскольку подпись List#toSet есть

def toSet[B >: A] => scala.collection.immutable.Set[B]

и компилятор должен будет вывести типы в двух местах вашего вызова. Альтернативой аннотации параметра в вашей функции будет вызов toSet с явным аргументом типа:

xs.toSet[Int] map (_*2)

ОБНОВЛЕНИЕ:

Что касается вашего вопроса, почему компилятор может сделать это в два этапа, давайте просто взглянем на то, что происходит, когда вы вводите строки один за другим:

scala> val xs = List(1,2,3)
xs: List[Int] = List(1, 2, 3)

scala> val ys = xs.toSet   
ys: scala.collection.immutable.Set[Int] = Set(1, 2, 3)

Здесь компилятор выберет наиболее специфический тип для ys, который в этом случае Set[Int]. Этот тип известен сейчас, поэтому можно указать тип функции, переданной в map.

Если вы заполнили все возможные параметры типа в своем примере, вызов будет записан как:

xs.toSet[Int].map[Int,Set[Int]](_*2)

где параметр второго типа используется для указания типа возвращаемой коллекции (подробнее смотрите, как реализованы коллекции Scala). Это означает, что я даже недооценил количество типов, которые должен сделать компилятор.

В этом случае может показаться легким сделать вывод Int, но бывают случаи, когда это не так (учитывая другие особенности Scala, такие как неявные преобразования, одноэлементные типы, черты как mixins и т.д.). Я не говорю, что этого не может быть сделано - это просто, что компилятор Scala этого не делает.