Ответ 1
Согласно Алексей Романов, в принципе тип можно было бы вывести автоматически, но просто не в настоящее время.
Итак, по крайней мере на данный момент, все параметры типа, которые должны быть выведены, должны явно отображаться в аргументах.
Пожалуйста, рассмотрите 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 и др.
Согласно Алексей Романов, в принципе тип можно было бы вывести автоматически, но просто не в настоящее время.
Итак, по крайней мере на данный момент, все параметры типа, которые должны быть выведены, должны явно отображаться в аргументах.
На самом деле он не отвечает "почему", извините, но вот еще несколько трюков, которые вы можете играть. Во-первых, поскольку вы не используете 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
, созданные так же, как вы всегда знали, что им нужно.
Компилятор не может автоматически вывести 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).
Таким образом, оба случая не эквивалентны.