Wyjaśnienie kontrawariancji typu zwracanego funkcji jako parametru funkcji zewnętrznego kontenera konwariantnego

Nov 23 2020

W opcji mamy

def getOrElse[B >: A](default: => B): B = this match {
        case None => default
        case Some(a) => a
    }
def orElse[B >: A](obj: => Option[B]): Option[B] = this match {
        case None => obj
        case _ => this
    }

W każdym z nich mamy:

def flatMap[EE >: E, B](f: A => Either[EE, B]): Either[EE, B]

Rozumiem, co się dzieje i dlaczego, raczej rozszerzonym przykładem może być to

OrElse ({Opcja [B]}). Map {....} Jeśli B jest takie, że A:> B, to jeśli Some (a) otrzymasz Some (a) .map (f: B => ??? ), a następnie Kaboom

ogólnie rzecz biorąc, myślę, że nie mam nic przeciwko wariancji. Czego nie widziałem ani nie odkryłem, ponieważ nie tego wyjaśnia prosty przykład współwariancji i kontrawariancji jako przypadki użycia i chciałbym potwierdzić tutaj:

Zwracany typ funkcji jako parametr jest sprawdzany pod kątem pozycji wariancji kontenera zewnętrznego.

Zwykle byłby to przykład

Container[+A] {
  def outerfunction[B](value: A): B
}

Następnie wyjaśniono nam, nie mogę, stanowisko przeciwwariancji dla A. Nie będę powtarzał pełnego wyjaśnienia, dlaczego. Załóżmy, że wszyscy to rozumiemy.

To, czego zwykle się nie wyjaśnia, to:

Container[+A] {
      def outerfunction(f: ??? => A): A
    }

Nie chodzi tylko o pobranie parametru typu A, ale także o pobranie dowolnego parametru funkcji, który zwraca to A. Kompilator dokłada wszelkich starań, aby to sprawdzić. Zastanawiam się, czy to się tutaj kończy, czy może jest to coś, co może wytworzyć A jako parametr funkcji kontenera.

Odpowiedzi

1 slouc Nov 23 2020 at 22:17

Twoje rozumienie jest całkowicie poprawne. Szczerze mówiąc, nie jestem pewien, o co dokładnie chodzi, ale zakładam, że tak - które miejsca kompilator sprawdza w przypadku:

trait Container[+A] {
  def outerfunction(f: String => A): A
}

A odpowiedź brzmi - wszystkie.

Więc kiedy kompilator zobaczy trait Container[+A], sprawdzi treść tego Containerdla wszystkich wystąpień A, aby sprawdzić, czy znajdują się one w:

  • pozycja parametru (która wprowadza sprzeczne wymaganie)
  • zwracana pozycja typu (wymaganie kowariantne)
  • oba (niezmienne wymaganie)
  • ani (tak zwana wariancja fantomowa ).

W takim przypadku Container[+A]będzie wymagało, aby wszystkie wystąpienia Abyły w kowariantnej pozycji, co oznacza, że ​​będzie miał problem z String => A.

To takie proste. Nie ma znaczenia, czy jest to „funkcja wewnętrzna” czy „funkcja zewnętrzna”.