Базовый тип инферральный - PullRequest
11 голосов
/ 05 января 2012

Пожалуйста, рассмотрите case class Foo[A, B <: List[A]](l: B) { ... } или что-то похожее.В частности, A, а также B должны быть доступны где-то в теле Foo.

Возможно ли, чтобы компилятор выводил A автоматически?Например, Foo(List(1,2,3)) завершается ошибкой, поскольку средство проверки типа выводит A как Nothing.Возможно, есть способ использовать члены типа для решения этой проблемы?

У меня есть определенное чувство, что я упускаю из виду нечто невероятно простое;)

РЕДАКТИРОВАТЬ: я только что узнал, что спараметр другого типа X работает просто отлично, но я не понимаю, почему это так:

scala> case class Bar[A, B[X] <: List[X]](l: B[A])
defined class Bar

scala> Bar(List(1,2,3))
res11: Bar[Int,List] = Bar(List(1, 2, 3))

Может кто-нибудь объяснить мне это? Это проблема объединения?

РЕДАКТИРОВАТЬ 2: Использование [A, B[X] <: List[X]](l: B[A]) может иметь нежелательные последствия для определенных иерархий (хотя на самом деле это не имеет большого значения).Что еще интереснее, я только что наткнулся на сообщение в блоге Джоша Суерета, в котором неявно показано, что [A, B <: List[A]](l: B with List[A]) работает так же хорошо ... Нет необходимости в подтекстах и ​​т. Д.

Ответы [ 3 ]

5 голосов
/ 08 января 2012

Компилятор не может автоматически определить A. Но если бы это было возможно, пришлось бы сказать, что A должен быть супертипом Int, то есть Int или Any, а не просто Int !. Потому что список [Int] <: список [любой]. Так что, если компилятор выведет Int для A, это будет слишком ограничительным. </p>

Другими словами:

  1. case class Foo[A, B <: List[A]](l: B) { ... } и затем, называя его Foo(List(1,2,3)), вы говорите, что A должен быть типом, для которого указано, что его список должен быть супертипом списка Int . Таким образом, A должен быть супертипом Int, поскольку List является ковариантным.

  2. case class Bar[A, B[X] <: List[X]](l: B[A]) и затем, называя его Foo(List(1,2,3)), вы говорите, что A должно быть Int .

Случай (2) не оставляет места для А быть чем-то еще, кроме Int. Случай (1), тем не менее, оставляет место для A, чтобы быть чем-то иным, чем Int, например, Это может быть любой. Вы можете видеть это, потому что вы можете позвонить по Foo[Any, List[Int]](List(1,2,3)). Вы не сможете сделать это в случае (2).

Так что два случая не эквивалентны.

5 голосов
/ 06 января 2012

Это действительно не отвечает на вопрос «почему», извините, но вот еще несколько уловок, которые вы можете сыграть.Во-первых, поскольку вы не используете X в своем примере, вы можете написать:

case class Bar[A,B[_] <: Seq[_]](l : B[A])

, а затем:

scala> Bar(List(1,2,3))
resN: Bar[Int,List] = Bar(List(1, 2, 3))

(я использую ковариант Seq вместо List, чтобы показать, что он работает и для подтипов. Также обратите внимание, что это не эквивалентно использованию дополнительного X, см. комментарии.) К сожалению, каждый раз, когда вы хотите использовать тип последовательности, вам нужнонаписать B[A], т.е. создать его вручную.Один из способов обойти это - написать:

case class Bar[A,B](l : B)(implicit ev : B <:< Seq[A])

В действии:

scala> Bar(List(1,2,3))
resN: Bar[Int,List[Int]] = Bar(List(1, 2, 3))

... и вы получите параметры типа A и B, созданные простокак ты всегда знал, что они должны.

2 голосов
/ 14 февраля 2012

Согласно Алексею Романову , в принципе тип может быть выведен автоматически, но в настоящее время просто нет.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...