Mengapa kompilator Scala tidak dapat memberikan peringatan pencocokan pola untuk kelas / sifat yang tidak ditutup?

10

Jika saya menggunakan un disegel traitatau abstract classdalam Scala dan kemudian menggunakan pencocokan pola, saya bertanya-tanya, apakah kompiler tidak tahu pada waktu kompilasi untuk patternmatch khusus ini apa implementasi yang mungkin dari sifat / kelas ini yang tersedia? Jadi, jika ya, bisakah itu tidak memberikan peringatan kecocokan pola meskipun trait/ abstract classtidak disegel karena dia tahu jenis mana yang dapat digunakan, dengan memeriksa semua kemungkinan dependensi / impor?

Misalnya jika saya memiliki Option[A]dan saya melakukan pencocokan pola hanya untuk Some[A]tetapi tidak untuk None, kompiler akan mengeluh, karena Optiondisegel.

Jika kompiler tidak dapat mengetahui / menyelesaikannya, lalu mengapa dia tidak bisa? Dan jika kompiler (secara teoritis) dapat melakukan itu, apa alasan untuk itu tidak digunakan dalam Scala? Apakah ada bahasa lain yang mendukung perilaku semacam itu?

valenterri
sumber
Tidak jelas apa yang Anda minta. Apakah Anda ingin kompiler mengeluarkan peringatan jika ekspresi kecocokan tidak mencakup semua input yang mungkin? Mungkin sebuah contoh akan membuat pertanyaan Anda lebih jelas.
kdgregory
2
Jika ada yang bisa memperkenalkan subclass baru, kecocokan pola tidak akan pernah lengkap. Misalnya Anda menghasilkan beberapa kelas abstrak Foodengan subclass A, Bdan C, dan semua pertandingan pola cocok hanya tiga. Tidak ada yang menghentikan saya untuk menambahkan subclass baru Dyang akan meledakkan kecocokan pola Anda.
Doval
@kdgregory Ya, Anda mengerti. Saya menambahkan contoh untuk membuatnya lebih jelas.
valenterri
3
Memeriksa semua impor tidak cukup untuk menemukan semua subkelas yang mungkin. Subclass lain mungkin dideklarasikan dalam file kelas terpisah yang kemudian dimuat selama runtime via java.lang.ClassLoader.
amon

Jawaban:

17

Mencari tahu semua subclass dari suatu kelas disebut Analisis Hirarki Kelas, dan melakukan CHA statis dalam bahasa dengan pemuatan kode dinamis setara dengan menyelesaikan Masalah Pemutusan.

Plus, salah satu tujuan Scala adalah kompilasi dan penyebaran modul independen yang terpisah, sehingga kompiler tidak dapat mengetahui apakah suatu kelas disubklasifikasi dalam modul lain, karena ia tidak pernah melihat lebih dari satu modul. (Setelah semua, Anda dapat mengkompilasi modul terhadap antarmuka beberapa modul lain tanpa modul itu ada di sistem Anda!) Itu sebabnya sealedmengharuskan semua subclass harus didefinisikan dalam unit kompilasi yang sama.

Itu juga salah satu alasan mengapa JVM dapat bersaing dengan baik dengan kompiler C ++: Kompiler C ++ biasanya adalah kompiler statis, sehingga mereka secara umum tidak dapat mengetahui apakah suatu metode ditimpa atau tidak, dan dengan demikian tidak dapat menyejajarkannya. JVM OTOH, biasanya adalah kompiler dinamis, mereka tidak perlu melakukan CHA untuk mengetahui apakah suatu metode ditimpa atau tidak, mereka hanya dapat melihat Hierarki Kelas saat runtime. Dan bahkan jika pada titik selanjutnya dalam pelaksanaan program subclass baru datang yang tidak ada sebelumnya, bukan masalah besar, hanya mengkompilasi ulang potongan kode tanpa inline.

Catatan: semua ini hanya berlaku dalam Scala. JVM tidak memiliki gagasan tentang sealed, jadi sangat mungkin untuk mensubkelas sealedkelas dari bahasa JVM lain , karena tidak ada cara untuk mengkomunikasikan hal ini ke bahasa lain. The sealedproperti dicatat dalam ScalaSigpenjelasan, tapi kompiler bahasa lain tidak mengambil orang-orang penjelasan ke rekening, jelas.

Jörg W Mittag
sumber
3

Hal ini dapat dilakukan (setidaknya untuk semua kelas yang diketahui pada waktu kompilasi), itu hanya mahal. Anda benar-benar akan menghancurkan kompilasi tambahan, karena semua yang berisi kecocokan pola harus dikompilasi ulang setiap kali file lain diubah.

Dan apa yang kamu beli? Adalah bau kode untuk menulis kecocokan pola yang perlu sering diubah ketika kelas turunan baru ditambahkan. Ini merupakan pelanggaran prinsip terbuka / tertutup . Gunakan warisan dengan benar dan Anda tidak perlu menulis jenis kecocokan pola tersebut. Dan ya, prinsip terbuka / tertutup juga berlaku untuk bahasa fungsional tanpa pewarisan berbasis kelas. Bahkan di antara fitur-fitur seperti kelas tipe, multimethod, dan sekadar fungsi tingkat tinggi, bahasa fungsional membuat ekstensi tanpa modifikasi jauh lebih mudah.

Karl Bielefeldt
sumber
1
It can be done (at least for all classes known at compile time), it's just expensive.Tetapi jika program ini tidak 100% mandiri (yaitu tergantung pada .jarfile eksternal ), tidak bisakah Anda menyelinap di subkelas baru setelah kompilasi melalui salah satu dari jars? Jadi kompilator dapat memberi tahu Anda "Kecocokan pola Anda sudah lengkap sekarang, tetapi itu dapat berubah jika ada dari dependensi Anda yang berubah" yang sangat tidak berharga karena titik memiliki dependensi eksternal adalah untuk dapat memperbaruinya tanpa kompilasi ulang!
Doval
Karena itu pelepasan tanggung jawab hukum. Dalam praktiknya, jika Anda cukup berpasangan sehingga membutuhkan kecocokan lengkap pada ketergantungan, eksternal atau sebaliknya, Anda tetap ingin mengkompilasi ulang.
Karl Bielefeldt
@Doval Kemudian Anda harus menggunakan beberapa bentuk delegasi dan membiarkan kelas dipanggil memutuskan apa yang harus dilakukan dan membalikkan kontrol. Inilah yang dimaksudkan untuk OOP. Jika Anda tidak menginginkannya maka Anda memiliki masalah saat ini.
Kereta luncur
@ArtB Saya gagal melihat apa yang harus dilakukan oleh delegasi atau inversi kontrol dengan ini.
Doval
Jika Anda ingin itu berfungsi dengan orang-orang yang dapat menambahkannya secara eksternal, maka panggil kelas dan pindahkan logika ke kelas-kelas itu.
Kereta luncur