Jenis eksistensial tidak benar-benar dianggap praktik buruk dalam pemrograman fungsional. Saya pikir apa yang membuat Anda tersandung adalah bahwa salah satu kegunaan yang paling sering dikutip untuk eksistensial adalah antipattern typeclass antrian , yang banyak orang percaya adalah praktik buruk.
Pola ini sering digunakan sebagai jawaban atas pertanyaan tentang bagaimana memiliki daftar elemen yang diketik secara heterogen yang semuanya menerapkan typeclass yang sama. Misalnya, Anda mungkin ingin memiliki daftar nilai yang memiliki Show
instance:
{-# LANGUAGE ExistentialTypes #-}
class Shape s where
area :: s -> Double
newtype Circle = Circle { radius :: Double }
instance Shape Circle where
area (Circle r) = pi * r^2
newtype Square = Square { side :: Double }
area (Square s) = s^2
data AnyShape = forall x. Shape x => AnyShape x
instance Shape AnyShape where
area (AnyShape x) = area x
example :: [AnyShape]
example = [AnyShape (Circle 1.0), AnyShape (Square 1.0)]
Masalah dengan kode seperti ini adalah ini:
- Satu-satunya operasi yang berguna yang dapat Anda lakukan pada
AnyShape
adalah untuk mendapatkan wilayahnya.
- Anda masih perlu menggunakan
AnyShape
konstruktor untuk memasukkan salah satu tipe bentuk ke dalam AnyShape
tipe tersebut.
Jadi ternyata, potongan kode itu tidak benar-benar memberi Anda apa pun yang tidak lebih pendek:
class Shape s where
area :: s -> Double
newtype Circle = Circle { radius :: Double }
instance Shape Circle where
area (Circle r) = pi * r^2
newtype Square = Square { side :: Double }
area (Square s) = s^2
example :: [Double]
example = [area (Circle 1.0), area (Square 1.0)]
Dalam kasus kelas multi-metode, efek yang sama dapat secara umum dicapai lebih sederhana dengan menggunakan pengkodean "catatan metode" —daripada menggunakan sejenis kelas Shape
, Anda menentukan jenis rekaman yang bidangnya adalah "metode" dari Shape
jenis tersebut. , dan Anda menulis fungsi untuk mengonversi lingkaran dan kotak Anda menjadi Shape
s.
Tetapi itu tidak berarti bahwa tipe eksistensial adalah masalah! Sebagai contoh, di Rust mereka memiliki fitur yang disebut objek sifat yang sering digambarkan orang sebagai tipe eksistensial atas suatu sifat (versi kelas tipe Rust). Jika typeclasses eksistensial adalah antipattern di Haskell, apakah itu berarti Rust mengambil solusi yang buruk? Tidak! Motivasi di dunia Haskell adalah tentang sintaks dan kenyamanan, bukan tentang prinsip.
Cara yang lebih matematis menempatkan ini menunjukkan bahwa AnyShape
jenis dari atas dan Double
yang isomorfik -ada adalah "lossless konversi" di antara mereka (baik, menyimpan untuk floating point presisi):
forward :: AnyShape -> Double
forward = area
backward :: Double -> AnyShape
backward x = AnyShape (Square (sqrt x))
Jadi sebenarnya, Anda tidak mendapatkan atau kehilangan kekuatan dengan memilih satu vs yang lain. Yang berarti pilihan harus didasarkan pada faktor-faktor lain seperti kemudahan penggunaan atau kinerja.
Dan perlu diingat bahwa tipe eksistensial memiliki kegunaan lain di luar contoh daftar heterogen ini, jadi ada baiknya memilikinya. Misalnya, ST
tipe Haskell , yang memungkinkan kita untuk menulis fungsi yang secara eksternal murni tetapi menggunakan operasi mutasi memori secara internal, menggunakan teknik berdasarkan tipe eksistensial untuk menjamin keamanan pada waktu kompilasi.
Jadi jawaban umumnya adalah tidak ada jawaban umum. Penggunaan tipe eksistensial hanya dapat dinilai dalam konteks — dan jawabannya mungkin berbeda tergantung pada fitur dan sintaksis apa yang disediakan oleh berbagai bahasa.