Saya pikir masuk akal untuk menjelaskan tipe-tipe eksistensial bersama-sama dengan tipe universal, karena kedua konsep tersebut saling melengkapi, yaitu satu adalah "lawan" dari yang lain.
Saya tidak bisa menjawab setiap detail tentang tipe eksistensial (seperti memberikan definisi yang tepat, daftar semua kemungkinan penggunaan, hubungannya dengan tipe data abstrak, dll.) Karena saya tidak cukup berpengetahuan untuk itu. Saya hanya akan menunjukkan (menggunakan Java) apa yang dinyatakan oleh artikel HaskellWiki ini sebagai efek utama dari tipe eksistensial:
Jenis eksistensial dapat digunakan untuk beberapa tujuan berbeda. Tetapi apa yang mereka lakukan adalah 'menyembunyikan' variabel tipe di sisi kanan. Biasanya, semua jenis variabel yang muncul di sebelah kanan juga harus muncul di sebelah kiri [...]
Pengaturan contoh:
Pseudo-code berikut ini bukan Java yang cukup valid, meskipun akan cukup mudah untuk memperbaikinya. Sebenarnya, itulah tepatnya yang akan saya lakukan dalam jawaban ini!
class Tree<α>
{
α value;
Tree<α> left;
Tree<α> right;
}
int height(Tree<α> t)
{
return (t != null) ? 1 + max( height(t.left), height(t.right) )
: 0;
}
Biarkan saya menguraikan ini secara singkat untuk Anda. Kami mendefinisikan ...
tipe rekursif Tree<α>
yang mewakili node dalam pohon biner. Setiap node menyimpan a value
dari beberapa tipe α dan memiliki referensi ke opsional left
dan right
subtree dari tipe yang sama.
fungsi height
yang mengembalikan jarak terjauh dari simpul daun ke simpul akar t
.
Sekarang, mari kita ubah pseudo-code di atas height
menjadi sintaksis Java yang tepat! (Saya akan terus menghilangkan beberapa pelat ketel untuk kepentingan singkatnya, seperti pengubah orientasi objek dan aksesibilitas.) Saya akan menunjukkan dua solusi yang mungkin.
1. Solusi tipe universal:
Perbaikan yang paling jelas adalah dengan membuat height
generik dengan memasukkan parameter tipe α ke dalam tanda tangannya:
<α> int height(Tree<α> t)
{
return (t != null) ? 1 + max( height(t.left), height(t.right) )
: 0;
}
Ini akan memungkinkan Anda untuk mendeklarasikan variabel dan membuat ekspresi tipe α di dalam fungsi itu, jika Anda mau. Tapi...
2. Solusi tipe eksistensial:
Jika Anda melihat tubuh metode kami, Anda akan melihat bahwa kami tidak benar-benar mengakses, atau bekerja dengan, apa pun dari tipe α ! Tidak ada ekspresi yang memiliki tipe itu, juga tidak ada variabel yang dideklarasikan dengan tipe itu ... jadi, mengapa kita harus membuat height
generik sama sekali? Mengapa kita tidak bisa melupakan α saja ? Ternyata, kita dapat:
int height(Tree<?> t)
{
return (t != null) ? 1 + max( height(t.left), height(t.right) )
: 0;
}
Seperti yang saya tulis di awal jawaban ini, tipe eksistensial dan universal bersifat komplementer / ganda. Jadi, jika solusi tipe universal adalah membuat height
lebih umum, maka kita harus berharap bahwa tipe eksistensial memiliki efek sebaliknya: menjadikannya kurang generik, yaitu dengan menyembunyikan / menghapus parameter tipe α .
Sebagai konsekuensinya, Anda tidak bisa lagi merujuk pada tipe t.value
dalam metode ini atau memanipulasi ekspresi apa pun dari tipe itu, karena tidak ada pengidentifikasi yang terikat padanya. ( ?
Wildcard adalah token khusus, bukan pengenal yang "menangkap" suatu tipe.) Secara t.value
efektif menjadi buram; mungkin satu-satunya hal yang masih bisa Anda lakukan adalah mengetikkannya Object
.
Ringkasan:
===========================================================
| universally existentially
| quantified type quantified type
---------------------+-------------------------------------
calling method |
needs to know | yes no
the type argument |
---------------------+-------------------------------------
called method |
can use / refer to | yes no
the type argument |
=====================+=====================================