Несколько вопросов о ковариации в Scala - PullRequest
0 голосов
/ 09 января 2019

Я пытаюсь понять ковариацию в Scala, но не могу найти примеров, которые бы помогли мне с этой проблемой. У меня есть этот код:

    class GenericCellImm[+T] (val x: T) {}

и он хорошо компилируется, но когда я его использую

    class GenericCellMut[+T] (var x: T) { }

не компилируется. Почему я не могу использовать var (но я могу использовать val), когда я пишу этот код? Как я могу это исправить? Также здесь похожая ситуация

    abstract class Sequence[+A] {
    def append(x: Sequence[A]): Sequence[A]} 

В чем проблема?

Ответы [ 2 ]

0 голосов
/ 09 января 2019

яблоко - это фрукт.

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

Но изменяемый пакет с яблоками не изменяемый пакет с фруктами, потому что вы можете положить вещи в изменяемый пакет с фруктами. Например, банан. В сумке яблок разрешено содержать только яблоки, а не бананы!

И именно по этой причине Scala не допускает первую конструкцию.

А как насчет второго? Scala позволяет вам сделать это с некоторыми изменениями. Но результат противоречив. По сути, вы можете создать X[Fruit], который является своего рода X[Apple]. X немного похож на сумку, но работает в противоположном направлении. Что может быть Х, если мы придерживаемся аналогии с фруктами? Думайте об этом как соковыжималка. Вы можете положить яблоки в яблочную соковыжималку. Вы можете положить любой фрукт в соковыжималку. Как ни парадоксально, фруктовая соковыжималка - это своего рода яблочная соковыжималка! Единственное, что может сделать яблочная соковыжималка, - это сжимать яблоки. Но фруктовая соковыжималка тоже может это делать, что является основой отношений типа «подтип» (подтип).

0 голосов
/ 09 января 2019

Вы не можете написать def append(x: Sequence[A]): Sequence[A]}, так как A находится в контравариантной позиции в аргументе append, будучи одновременно ковариантным.

Вы должны написать это так:

abstract class Sequence[+A] {
   def append[B >: A](x: Sequence[B]): Sequence[B]
}

У вас есть отличное объяснение в Почему пример не компилируется, иначе как (со-, противо- и не-) дисперсия работает?

Короче говоря:

+ A заявляет, что безопасно преобразовать этот A в супертип A во всех контекстах (собака может быть преобразована в животное). Если вы пишете append[A](x: Sequence[A]), вы утверждаете, что x может быть только A или подтипами A (Dog, Yorsay и т. Д.), Но никогда не быть супертипом (например, Animal), поэтому это противоречит аннотации + A и завершается неудачно во время компиляции. С помощью сигнатуры append[B >: A](x: Sequence[B]) вы исправляете это, избегая именования A в аргументах функции.

Итак, [B >: A] определяет нижнюю границу для B, заявляя, что B должен быть супертипом A или A, но никогда не являясь классом ниже A в иерархии, и, следовательно, соответствует сигнатуре + A.

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

...