Perbedaan antara inferensi tipe metode dan parameter tipe kelas dalam pencocokan pola

9

Mengapa pencocokan pola bekerja berbeda ketika parameter type berasal dari metode melampirkan sebagai lawan dari kelas melampirkan? Sebagai contoh,

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

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

memberi kesalahan

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

sementara itu berhasil mengkompilasi kapan Aparameter tipe metode

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

Pertanyaannya didasarkan pada analisis Daniel , yang dulu saya coba berikan jawaban untuk pertanyaan serupa.

Mario Galic
sumber

Jawaban:

4

Saya tidak memiliki jawaban lengkap 100%, tetapi saya memiliki sebuah pointer yang mungkin cukup untuk Anda.

Kompilator Scala menangani GADT (Generalized Algebraic Data Type) dengan cara yang sangat khusus. Beberapa kasus diselesaikan dengan penanganan khusus, beberapa kasus tidak terpecahkan. Dotty sedang mencoba untuk mengisi sebagian besar lubang, dan telah menyelesaikan banyak masalah terkait, namun masih ada beberapa yang terbuka juga.

Contoh umum penanganan GADT khusus dalam kompiler Scala 2 sangat terkait dengan use case Anda. Jika kita melihat:

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

dan kami secara eksplisit mendeklarasikan tipe pengembalian menjadi A:

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

itu akan mengkompilasi dengan baik. IDE Anda mungkin mengeluh, tetapi kompiler akan membiarkannya lewat. Metode mengatakan mengembalikan sebuah A, tetapi kasus pencocokan pola mengevaluasi menjadi Int, yang secara teoritis tidak boleh dikompilasi. Namun, penanganan khusus GADT di kompiler mengatakan tidak apa-apa, karena dalam cabang pencocokan pola tertentu Atelah "diperbaiki" menjadi Int(karena kami cocok dengan Derivedyang a Base[Int]).

Parameter tipe umum untuk GADT (dalam kasus kami A) harus dinyatakan di suatu tempat. Dan inilah bagian yang menarik - penanganan kompiler khusus hanya berfungsi ketika dideklarasikan sebagai parameter tipe dari metode penutup . Jika itu berasal dari anggota tipe atau parameter tipe dari sifat / kelas terlampir, itu tidak dapat dikompilasi, seperti yang Anda saksikan sendiri.

Inilah sebabnya saya mengatakan itu bukan jawaban lengkap 100% - Saya tidak bisa menunjuk ke tempat konkret (seperti spesifikasi resmi) yang mendokumentasikan ini dengan benar. Sumber tentang penanganan GADT di Scala berasal dari beberapa blogpost , yang bagus, tetapi jika Anda menginginkan lebih dari itu, Anda harus menggali sendiri kode kompiler. Saya mencoba melakukan hal itu, dan saya pikir ini berkaitan dengan metode ini , tetapi jika Anda benar-benar ingin masuk lebih dalam, Anda mungkin ingin melakukan ping ke seseorang yang lebih berpengalaman dengan basis kode penyusun Scala.

Slouc
sumber