Обобщения Scala - Почему Scala возвращает экземпляр супертипа вместо подтипа, когда используется ограничение типа?

Я пытаюсь преобразовать y во что-то, что может быть добавлено к x, где x - последовательность некоторых видов.

scala> def foo[U <: Seq[T], T](x: U, y: T): U = x :+ y
<console>:7: error: type mismatch;
 found   : Seq[T]
 required: U
       def foo[U <: Seq[T], T](x: U, y: T): U = x :+ y
                                                  ^

У меня есть следующие решения:

def foo[T]( x : Seq[T], y:T) = x :+ y
def foo[T]( x : Seq[T], y:T) : Seq[T] = x :+ y
def foo[U <: Seq[T], T](x: U, y: T): U = (x :+ y).asInstanceOf[U]

Но я сомневаюсь, почему оригинальный не работал. Похоже, я применяю оператор (:+ в этом случае) определен в суперклассе, тогда он возвращает суперкласс? т.е. если U это Vector, foo возвращается Seqтак я получаю ошибку required "U" but found "Seq[T]",

Может ли кто-нибудь просветить меня, почему это поведение замечено?

2 ответа

Решение

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

В вашем случае, если исходный вариант - "Хорошо".

 def foo[U <: Seq[T], T](x: U, y: T): U = x :+ y

причина Seq[T] ковариантна относительно T, поэтому следующий случай имеет место.

 for type A, T, if A <: T, List[A] <: Seq[T]

Затем мы можем сделать следующую операцию:

 class Parent 
 class Child extends Parent

 // List(new Child) :+ (new Parent) => List[Parent]
val result = foo(List(new Child), new Parent)

На самом деле U - это List[Child] в методе foo, но когда List работает с типом, отличным от его типа элемента, он попытается найти общего родителя, в этом случае результат набирается с помощью List[Parent], но необходимого типа. Список [Ребенок]. Очевидно, List[Parent] не является подтипом List[Child].

Итак, дело в том, что конечный тип повышен, но обязательный тип является подтипом повышенного типа. Если вы посмотрите на определение Scala SeqLike, это может быть яснее.

trait SeqLike[+A, +Repr] extends ... {
    def :+[B >: A, That](elem: B)(...): That = {
       ...
    }
}

Упростим этот пример

  class T
  class B extends T

  def bar[U <: T](x: T): U = {
    new B
  }

Это не скомпилируется, потому что когда вы звоните

bar(new T)

вы должны вернуть тип T, но вы пытаетесь вернуть тип B. B является подтипом T, но вы должны возвращать именно U, а не просто подтип, если T.

Вы можете решить вашу проблему,

def foo[U <: Seq[T], T](x: U, y: T): Seq[T] = x :+ y

или же

def foo[B >: Seq[T], U <: Seq[T], T](x: U, y: T): B = y +: x
Другие вопросы по тегам