Różnica między wnioskowaniem typu metody i parametrów typu klasy w dopasowaniu wzorca

9

Dlaczego dopasowanie wzorca działa inaczej, gdy parametr type pochodzi z otaczającej metody, a nie z otaczającej klasy? Na przykład,

trait Base[T]
case class Derived(v: Int) extends Base[Int]

class Test[A] {
  def method(arg: Base[A]) = {
    arg match {
      case Derived(_) => 42
    }
  }
}

daje błąd

constructor cannot be instantiated to expected type;
 found   : A$A87.this.Derived
 required: A$A87.this.Base[A]
      case Derived(_) => 42
           ^

podczas gdy z powodzeniem kompiluje się, gdy Ajest parametrem typu metody

class Test {
  def method[A](arg: Base[A]) = {
    arg match {
      case Derived(_) => 42
    }
  }
}

Pytanie opiera się na analizie Daniela , której próbowałem udzielić odpowiedzi na podobne pytanie.

Mario Galic
źródło

Odpowiedzi:

4

Nie mam w 100% pełnej odpowiedzi, ale mam wskaźnik, który może być dla Ciebie wystarczający.

Kompilator Scala zajmuje się GADT (uogólnionymi typami danych algebraicznych) w bardzo szczególny sposób. Niektóre sprawy są rozwiązywane za pomocą specjalnej obsługi, niektóre sprawy są nierozwiązane. Dotty próbuje wypełnić większość dziur i już rozwiązało wiele powiązanych problemów, jednak wciąż istnieje wiele otwartych problemów .

Typowy przykład specjalnej obsługi GADT w kompilatorze Scala 2 jest bardzo związany z twoim przypadkiem użycia. Jeśli spojrzymy na:

def method[A](arg: Base[A]) = {
  arg match {
    case Derived(_) => 42
  }
}

i wyraźnie deklarujemy, że typem zwrotu jest A:

def method[A](arg: Base[A]): A 

skompiluje się dobrze. Twoje IDE może narzekać, ale kompilator to przepuści. Metoda mówi, że zwraca an A, ale wielkość pasująca do wzorca zmienia się w Int, która teoretycznie nie powinna się kompilować. Jednak specjalna obsługa GADT w kompilatorze mówi, że jest w porządku, ponieważ w tym konkretnym odgałęzieniu dopasowywania wzorca Azostała „ustalona” na Int(ponieważ dopasowaliśmy, na Derivedktórym jest a Base[Int]).

Ogólny parametr typu GADT (w naszym przypadku A) musi być gdzieś zadeklarowany. A oto interesująca część - specjalna obsługa kompilatora działa tylko wtedy, gdy jest zadeklarowana jako parametr typu metody zamykającej . Jeśli pochodzi od elementu typu lub parametru typu obejmującej cechy / klasy, nie kompiluje się, jak sam to widziałeś.

Dlatego powiedziałem, że nie jest to w 100% kompletna odpowiedź - nie mogę wskazać konkretnego miejsca (takiego jak oficjalna specyfikacja), które odpowiednio to dokumentuje. Źródła na manipulacyjny GADTs w Scala sprowadzają się do kilku z blogposts , które są świetne na drodze, ale jeśli chcesz więcej niż trzeba będzie kopać w kodzie kompilatora samodzielnie. Próbowałem zrobić dokładnie to i myślę, że sprowadza się to do tej metody , ale jeśli naprawdę chcesz zejść głębiej, możesz pingować kogoś bardziej doświadczonego z bazą kodową kompilatora Scala.

slouc
źródło