Saya rasa saya tidak mengerti kelas tipe. Saya pernah membaca di suatu tempat bahwa memikirkan kelas tipe sebagai "antarmuka" (dari OO) yang mengimplementasikan tipe adalah salah dan menyesatkan. Masalahnya adalah, saya mengalami masalah melihat mereka sebagai sesuatu yang berbeda dan bagaimana itu salah.
Misalnya, jika saya memiliki kelas tipe (dalam sintaks Haskell)
class Functor f where
fmap :: (a -> b) -> f a -> f b
Bagaimana itu berbeda dari antarmuka [1] (dalam sintaksis Java)
interface Functor<A> {
<B> Functor<B> fmap(Function<B, A> fn)
}
interface Function<Return, Argument> {
Return apply(Argument arg);
}
Satu perbedaan yang mungkin saya pikirkan adalah bahwa implementasi kelas tipe yang digunakan pada doa tertentu tidak ditentukan tetapi ditentukan dari lingkungan - katakanlah, memeriksa modul yang tersedia untuk implementasi untuk tipe ini. Itu tampaknya merupakan artefak implementasi yang dapat diatasi dalam bahasa OO; seperti kompiler (atau runtime) dapat memindai pembungkus / extender / monyet-patcher yang memperlihatkan antarmuka yang diperlukan pada jenisnya.
Apa yang saya lewatkan?
[1] Perhatikan bahwa f a
argumen telah dihapus fmap
sejak diberikan sebagai bahasa OO, Anda akan memanggil metode ini pada objek. Antarmuka ini menganggap f a
argumen telah diperbaiki.
C
tanpa kehadiran downcast?Selain jawaban Andreas yang sangat baik, harap diingat bahwa kelas tipe dimaksudkan untuk merampingkan kelebihan beban , yang memengaruhi ruang nama global. Tidak ada kelebihan muatan di Haskell selain dari apa yang dapat Anda peroleh melalui kelas tipe. Sebaliknya, ketika Anda menggunakan antarmuka objek, hanya fungsi-fungsi yang dideklarasikan untuk mengambil argumen dari antarmuka itu yang perlu khawatir tentang nama fungsi di antarmuka itu. Jadi, antarmuka menyediakan ruang nama lokal.
Misalnya, Anda memiliki
fmap
antarmuka objek yang disebut "Functor". Akan sangat baik untuk memiliki yang lainfmap
di antarmuka lain, katakanlah "Structor". Setiap objek (atau kelas) dapat memilih dan memilih antarmuka mana yang ingin diimplementasikan. Sebaliknya, di Haskell, Anda hanya dapat memiliki satu difmap
dalam konteks tertentu. Anda tidak dapat mengimpor kelas tipe Functor dan Structor ke konteks yang sama.Antarmuka objek lebih mirip dengan tanda tangan ML Standar daripada tipe kelas.
sumber
Dalam contoh konkret Anda (dengan kelas jenis Functor), implementasi Haskell dan Java berperilaku berbeda. Bayangkan Anda memiliki tipe data Mungkin dan ingin menjadi Functor (itu benar-benar tipe data populer di Haskell, yang dapat Anda implementasikan dengan mudah di Java juga). Dalam contoh Java Anda, Anda akan membuat Mungkin kelas mengimplementasikan antarmuka Functor Anda. Jadi, Anda dapat menulis yang berikut ini (hanya kode semu karena saya memiliki latar belakang c #):
Perhatikan bahwa
res
memiliki tipe Functor, bukan Maybe. Jadi ini membuat implementasi Java hampir tidak dapat digunakan karena Anda kehilangan informasi jenis konkret, dan perlu melakukan gips. (setidaknya saya gagal menulis implementasi seperti itu di mana jenis masih ada). Dengan kelas tipe Haskell Anda akan mendapatkan Maybe Int sebagai hasilnya.sumber
Maybe<Int>
.