Основной тип ввода

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

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

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

EDIT: я только выяснил, что использование другого параметра X работает просто отлично, но atm Я не понимаю, почему это так:

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]) может иметь нежелательные последствия для определенных иерархий (хотя это не очень важно). Более интересно, я просто наткнулся на сообщение в блоге от Josh Suereth, что неявно показывает, что [A, B <: List[A]](l: B with List[A]) работает так же хорошо... Нет необходимости в implicits и др.

Ответ 1

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

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

Ответ 2

На самом деле он не отвечает "почему", извините, но вот еще несколько трюков, которые вы можете играть. Во-первых, поскольку вы не используете 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))

(Я использую covariant 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, созданные так же, как вы всегда знали, что им нужно.

Ответ 3

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

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

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

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

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

Таким образом, оба случая не эквивалентны.