Saya membaca artikel Wikipedia jenis Eksistensial . Saya mengetahui bahwa mereka disebut tipe eksistensial karena operator eksistensial (∃). Saya tidak yakin apa gunanya itu. Apa bedanya
T = ∃X { X a; int f(X); }
dan
T = ∀x { X a; int f(X); }
?
Jawaban:
Ketika seseorang mendefinisikan tipe universal yang
∀X
mereka katakan: Anda dapat memasukkan tipe apa pun yang Anda inginkan, saya tidak perlu tahu apa-apa tentang tipe itu untuk melakukan pekerjaan saya, saya hanya akan merujuknya secara tidak jelasX
.Ketika seseorang mendefinisikan tipe eksistensial yang
∃X
mereka katakan: Saya akan menggunakan tipe apa pun yang saya inginkan di sini; Anda tidak akan tahu apa-apa tentang jenisnya, jadi Anda hanya dapat menyebutnya secara tidak jelasX
.Tipe universal memungkinkan Anda menulis hal-hal seperti:
The
copy
Fungsi tidak tahu apaT
sebenarnya akan, tetapi tidak perlu.Jenis eksistensial memungkinkan Anda menulis hal-hal seperti:
Setiap implementasi mesin virtual dalam daftar dapat memiliki tipe bytecode yang berbeda. The
runAllCompilers
Fungsi tidak tahu apa jenis bytecode, tapi itu tidak perlu; semua yang dilakukannya adalah menyampaikan bytecode dariVirtualMachine.compile
keVirtualMachine.run
.Wildcard tipe Java (ex:)
List<?>
adalah bentuk yang sangat terbatas dari tipe eksistensial.Pembaruan: Lupa menyebutkan bahwa Anda dapat menyortir jenis eksistensial dengan tipe universal. Pertama, bungkus tipe universal Anda untuk menyembunyikan parameter tipe. Kedua, kontrol terbalik (ini secara efektif menukar bagian "Anda" dan "Saya" dalam definisi di atas, yang merupakan perbedaan utama antara eksistensial dan universal).
Sekarang kita dapat memiliki
VMWrapper
panggilan kita sendiriVMHandler
yang memilikihandle
fungsi yang diketik secara universal . Efek bersihnya sama, kode kita harus diperlakukanB
sebagai buram.Contoh implementasi VM:
sumber
List<∃B:VirtualMachine<B>> vms
ataufor (∃B:VirtualMachine<B> vm : vms)
. (Karena ini adalah jenis generik, bisa tidak Anda telah menggunakan Jawa?
wildcard bukannya sintaks "buatan sendiri"?) Saya pikir mungkin membantu untuk memiliki contoh kode di mana tidak ada jenis generik seperti∃B:VirtualMachine<B>
yang terlibat, melainkan sebuah "lurus"∃B
, karena tipe generik mudah dikaitkan dengan tipe universal setelah contoh kode pertama Anda.∃B
secara eksplisit tentang di mana kuantifikasi terjadi. Dengan sintaks wildcard kuantifier tersirat (List<List<?>>
sebenarnya berarti∃T:List<List<T>>
dan tidakList<∃T:List<T>>
). Juga, kuantifikasi eksplisit memungkinkan Anda merujuk ke tipe (saya memodifikasi contoh untuk mengambil keuntungan dari ini dengan menyimpan bytecode jenisB
dalam variabel sementara).Nilai sejenis seperti eksistensial
∃x. F(x)
adalah pasangan yang mengandung beberapa jenisx
dan nilai jenisF(x)
. Sedangkan nilai sejenis polimorfik∀x. F(x)
adalah fungsi yang mengambil beberapa jenisx
dan menghasilkan nilai jenisF(x)
. Dalam kedua kasus, tipe menutup beberapa konstruktor tipeF
.Perhatikan bahwa tampilan ini memadukan jenis dan nilai. Bukti eksistensial adalah satu jenis dan satu nilai. Bukti universal adalah seluruh keluarga nilai yang diindeks berdasarkan tipe (atau pemetaan dari tipe ke nilai).
Jadi perbedaan antara dua jenis yang Anda tentukan adalah sebagai berikut:
Ini berarti: Nilai tipe
T
berisi tipe yang disebutX
, nilaia:X
, dan fungsif:X->int
. Produser nilai tipeT
dapat memilih tipe apa punX
dan konsumen tidak dapat mengetahui apa pun tentangnyaX
. Kecuali bahwa ada satu contoh yang dipanggila
dan nilai ini dapat diubah menjadiint
dengan memberikannya kepadaf
. Dengan kata lain, nilai tipeT
tahu cara menghasilkanint
entah bagaimana. Yah, kita bisa menghilangkan jenis perantaraX
dan hanya mengatakan:Yang dikuantifikasi secara universal sedikit berbeda.
Ini berarti: Nilai tipe
T
dapat diberikan tipe apa punX
, dan itu akan menghasilkan nilaia:X
, dan fungsif:X->int
apa pun yangX
ada . Dengan kata lain: konsumen nilai tipeT
dapat memilih tipe apa punX
. Dan penghasil nilai tipeT
tidak dapat mengetahui apa pun tentangnyaX
, tetapi harus dapat menghasilkan nilaia
untuk pilihan apa punX
, dan dapat mengubah nilai tersebut menjadiint
.Jelas menerapkan jenis ini tidak mungkin, karena tidak ada program yang dapat menghasilkan nilai dari setiap jenis yang bisa dibayangkan. Kecuali Anda mengizinkan absurditas seperti
null
atau pantat.Karena eksistensial adalah pasangan, argumen eksistensial dapat dikonversi menjadi argumen universal melalui currying .
sama dengan:
Yang pertama adalah eksistensial peringkat-2 . Ini mengarah ke properti bermanfaat berikut:
Ada algoritma standar untuk mengubah eksistensi menjadi universal, yang disebut Skolemization .
sumber
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:
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!
Biarkan saya menguraikan ini secara singkat untuk Anda. Kami mendefinisikan ...
tipe rekursif
Tree<α>
yang mewakili node dalam pohon biner. Setiap node menyimpan avalue
dari beberapa tipe α dan memiliki referensi ke opsionalleft
danright
subtree dari tipe yang sama.fungsi
height
yang mengembalikan jarak terjauh dari simpul daun ke simpul akart
.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: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: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.) Secarat.value
efektif menjadi buram; mungkin satu-satunya hal yang masih bisa Anda lakukan adalah mengetikkannyaObject
.Ringkasan:
sumber
Object
cukup menarik: Meskipun keduanya serupa dalam hal mereka memungkinkan Anda untuk menulis kode independen-jenis statis, mantan (generik) tidak hanya membuang hampir semua informasi jenis yang tersedia untuk mencapai tujuan ini. Dalam hal ini, obat generik adalah obat untukObject
IMO.public static void swap(List<?> list, int i, int j) { swapHelper(list, i, j); } private static <E> void swapHelper(List<E> list, int i, int j) { list.set(i, list.set(j, list.get(i))); }
,E
apakah auniversal type
dan?
mewakiliexistential type
??
dalam jenisint height(Tree<?> t)
ini masih belum diketahui di dalam fungsi, dan masih ditentukan oleh pemanggil karena pemanggil yang harus memilih pohon untuk lulus dalam. Bahkan jika orang menyebut ini jenis eksistensial di Jawa, tidak. The?
placeholder dapat digunakan untuk menerapkan bentuk existentials di Jawa, dalam beberapa keadaan, tapi ini bukan salah satu dari mereka.Ini semua adalah contoh yang baik, tetapi saya memilih untuk menjawabnya sedikit berbeda. Ingat dari matematika, ∀x itu. P (x) berarti "untuk semua x, saya dapat membuktikan bahwa P (x)". Dengan kata lain, ini adalah semacam fungsi, Anda memberi saya x dan saya punya metode untuk membuktikannya untuk Anda.
Dalam teori tipe, kita tidak berbicara tentang bukti, tetapi tentang tipe. Jadi dalam ruang ini yang kami maksud "untuk semua tipe X yang Anda berikan kepada saya, saya akan memberikan Anda tipe P tertentu". Sekarang, karena kami tidak memberikan P banyak informasi tentang X selain fakta bahwa itu adalah tipe, P tidak bisa berbuat banyak dengannya, tetapi ada beberapa contoh. P dapat membuat jenis "semua pasangan dari jenis yang sama":
P<X> = Pair<X, X> = (X, X)
. Atau kita dapat membuat jenis opsiP<X> = Option<X> = X | Nil
:, di mana Nil adalah jenis pointer nol. Kita bisa membuat daftar dari itu:List<X> = (X, List<X>) | Nil
. Perhatikan bahwa yang terakhir adalah rekursif, nilai-nilaiList<X>
adalah pasangan mana elemen pertama adalah X dan elemen kedua adalah aList<X>
atau yang lain itu adalah pointer nol.Sekarang, dalam matematika ∃x. P (x) berarti "Saya dapat membuktikan bahwa ada x tertentu sehingga P (x) benar". Mungkin ada banyak x, tetapi untuk membuktikannya, satu sudah cukup. Cara lain untuk memikirkannya adalah bahwa harus ada set pasangan bukti-dan-bukti yang tidak kosong {(x, P (x))}.
Diterjemahkan ke teori tipe: Tipe dalam keluarga
∃X.P<X>
adalah tipe X dan tipe yang sesuaiP<X>
. Perhatikan bahwa ketika sebelum kita memberikan X ke P, (sehingga kita tahu segalanya tentang X tetapi sangat sedikit P) bahwa kebalikannya benar sekarang.P<X>
tidak berjanji untuk memberikan informasi apa pun tentang X, hanya ada satu, dan itu memang tipe.Bagaimana ini berguna? Nah, P bisa menjadi tipe yang memiliki cara mengekspos tipe internal X. Contohnya adalah objek yang menyembunyikan representasi internal negara X. Meskipun kita tidak memiliki cara memanipulasi secara langsung, kita dapat mengamati efeknya dengan menyodok di P. Mungkin ada banyak implementasi jenis ini, tetapi Anda bisa menggunakan semua jenis ini tidak peduli mana yang dipilih.
sumber
P<X>
bukanP
(fungsi dan jenis wadah yang sama, katakanlah, tetapi Anda tidak tahu itu mengandungX
)?∀x. P(x)
tidak berarti apa-apa tentang provabilitasP(x)
, hanya kebenaran.Untuk langsung menjawab pertanyaan Anda:
Dengan tipe universal, penggunaan
T
harus menyertakan parameter tipeX
. MisalnyaT<String>
atauT<Integer>
. Untuk penggunaan tipe eksistensialT
jangan sertakan parameter tipe itu karena tidak diketahui atau tidak relevan - cukup gunakanT
(atau di JawaT<?>
).Informasi lebih lanjut:
Tipe universal / abstrak dan tipe eksistensial adalah dualitas perspektif antara konsumen / klien dari suatu objek / fungsi dan produsen / implementasi itu. Ketika satu sisi melihat tipe universal, yang lain melihat tipe eksistensial.
Di Jawa, Anda dapat menentukan kelas generik:
MyClass
,T
adalah universal karena Anda dapat mengganti jenis untukT
ketika Anda menggunakan kelas itu dan Anda harus mengetahui jenis sebenarnya dari T setiap kali Anda menggunakan sebuah instance dariMyClass
MyClass
itu sendiri,T
eksistensial karena tidak tahu jenis sebenarnyaT
?
mewakili tipe eksistensial - jadi ketika Anda berada di dalam kelas,T
pada dasarnya?
. Jika Anda ingin menangani instanceMyClass
denganT
existential, Anda dapat mendeklarasikanMyClass<?>
seperti padasecretMessage()
contoh di atas.Jenis eksistensial kadang-kadang digunakan untuk menyembunyikan detail implementasi sesuatu, seperti yang dibahas di tempat lain. Versi Java ini mungkin terlihat seperti:
Agak sulit untuk menangkap ini dengan benar karena saya berpura-pura berada dalam semacam bahasa pemrograman fungsional, yang Java tidak. Tetapi intinya di sini adalah bahwa Anda menangkap semacam negara ditambah daftar fungsi yang beroperasi pada negara itu dan Anda tidak tahu jenis sebenarnya dari bagian negara, tetapi fungsi lakukan karena mereka sudah cocok dengan jenis yang sudah .
Sekarang, di Jawa semua tipe non-primitif non-final sebagian eksistensial. Ini mungkin terdengar aneh, tetapi karena variabel yang dinyatakan
Object
berpotensi menjadi subkelasObject
, Anda tidak dapat mendeklarasikan tipe spesifik, hanya "tipe ini atau subkelas". Jadi, objek direpresentasikan sebagai sedikit status ditambah daftar fungsi yang beroperasi pada kondisi tersebut - fungsi mana yang akan dipanggil ditentukan saat runtime oleh pencarian. Ini sangat mirip dengan penggunaan tipe eksistensial di atas di mana Anda memiliki bagian kondisi eksistensial dan fungsi yang beroperasi pada kondisi itu.Dalam bahasa pemrograman yang diketik secara statis tanpa subtyping dan gips, tipe eksistensial memungkinkan seseorang untuk mengelola daftar objek yang diketik berbeda. Daftar
T<Int>
tidak dapat berisi aT<Long>
. Namun, daftarT<?>
dapat berisi variasi apa punT
, yang memungkinkan seseorang untuk memasukkan berbagai jenis data ke dalam daftar dan mengubahnya semuanya menjadi int (atau melakukan operasi apa pun yang disediakan di dalam struktur data) sesuai permintaan.Seseorang dapat hampir selalu mengubah catatan dengan tipe eksistensial menjadi catatan tanpa menggunakan penutupan. Penutupan juga diketik secara eksistensial, karena variabel bebas yang ditutupinya disembunyikan dari pemanggil. Dengan demikian bahasa yang mendukung penutupan tetapi bukan tipe eksistensial dapat memungkinkan Anda untuk membuat penutupan yang berbagi keadaan tersembunyi yang sama dengan yang Anda masukkan ke bagian eksistensial suatu objek.
sumber
Jenis eksistensial adalah jenis buram.
Pikirkan pegangan file di Unix. Anda tahu tipenya int, sehingga Anda dapat dengan mudah memalsunya. Anda dapat, misalnya, mencoba membaca dari pegangan 43. Jika kebetulan program memiliki file terbuka dengan pegangan khusus ini, Anda akan membacanya. Kode Anda tidak harus berbahaya, cukup ceroboh (mis., Pegangan bisa menjadi variabel yang tidak diinisialisasi).
Jenis eksistensial disembunyikan dari program Anda. Jika
fopen
mengembalikan tipe eksistensial, yang bisa Anda lakukan dengannya adalah menggunakannya dengan beberapa fungsi pustaka yang menerima tipe eksistensial ini. Sebagai contoh, pseudo-code berikut akan dikompilasi:Antarmuka "baca" dinyatakan sebagai:
Ada tipe T sedemikian rupa sehingga:
Exfile variabel bukan int, bukan a
char*
, bukan File struct — tidak ada yang dapat Anda ekspresikan dalam sistem tipe. Anda tidak bisa mendeklarasikan variabel yang tipenya tidak diketahui dan Anda tidak bisa melemparkan, katakanlah, sebuah pointer ke tipe yang tidak dikenal itu. Bahasa tidak akan membiarkan Anda.sumber
read
adalah∃T.read(T file, ...)
maka tidak ada yang dapat Anda lewati sebagai parameter pertama. Apa yang akan berhasil adalahfopen
mengembalikan pegangan file dan fungsi baca yang dicakup oleh eksistensial yang sama :∃T.(T, read(T file, ...))
Sepertinya saya datang agak terlambat, tetapi bagaimanapun, dokumen ini menambahkan pandangan lain tentang apa tipe-tipe eksistensial, walaupun tidak secara khusus agnostik-bahasa, seharusnya lebih mudah untuk memahami tipe-tipe eksistensial: http: //www.cs.uu .nl / groups / ST / Proyek / ehc / ehc-book.pdf (bab 8)
sumber
Tipe universal ada untuk semua nilai parameter tipe (s). Tipe eksistensial hanya ada untuk nilai parameter tipe yang memenuhi batasan tipe eksistensial.
Sebagai contoh dalam Scala salah satu cara untuk mengekspresikan tipe eksistensial adalah tipe abstrak yang dibatasi pada beberapa batas atas atau bawah.
Secara ekuivalen tipe universal yang dibatasi adalah tipe eksistensial seperti pada contoh berikut.
Setiap situs penggunaan dapat menggunakan
Interface
karena setiap subtipe instantiableExistential
harus mendefinisikantype Parameter
yang harus menerapkanInterface
.Sebuah kasus degenerasi dari jenis eksistensial di Scala adalah tipe abstrak yang tidak pernah disebut dan dengan demikian tidak perlu didefinisikan oleh subtipe apapun. Ini secara efektif memiliki notasi singkatan
List[_]
di Scala danList<?>
di Jawa.Jawaban saya terinspirasi oleh proposal Martin Odersky untuk menyatukan tipe abstrak dan eksistensial. Alat bantu slide yang menyertainya memahami.
sumber
∀x.f(x)
,, tidak jelas untuk fungsi penerimaannya sementara Tipe Eksistensial∃x.f(x)
,, dibatasi memiliki properti tertentu. Biasanya, semua parameter Eksistensial karena fungsinya akan memanipulasi mereka secara langsung; namun, parameter umum mungkin memiliki tipe yang bersifat Universal karena fungsi tersebut tidak akan mengelolanya di luar operasi universal dasar seperti memperoleh referensi seperti pada:∀x.∃array.copy(src:array[x] dst:array[x]){...}
forSome
untuk jenis parameter kuantifikasi eksistensial.Penelitian terhadap tipe data abstrak dan penyembunyian informasi membawa tipe eksistensial ke dalam bahasa pemrograman. Membuat abstrak tipe data menyembunyikan informasi tentang jenis itu, jadi klien jenis itu tidak dapat menyalahgunakannya. Katakanlah Anda memiliki referensi ke suatu objek ... beberapa bahasa memungkinkan Anda untuk memberikan referensi itu ke referensi ke byte dan melakukan apa pun yang Anda inginkan pada sepotong memori itu. Untuk tujuan menjamin perilaku suatu program, berguna bagi suatu bahasa untuk memastikan bahwa Anda hanya bertindak berdasarkan referensi ke objek melalui metode yang disediakan oleh perancang objek tersebut. Anda tahu jenisnya ada, tetapi tidak lebih.
sumber
Saya membuat diagram ini. Saya tidak tahu apakah ini keras. Tetapi jika itu membantu, saya senang.
sumber
Seperti yang saya mengerti itu adalah cara matematika untuk menggambarkan antarmuka / kelas abstrak.
Adapun T = ∃X {X a; int f (X); }
Untuk C # itu akan menerjemahkan ke tipe abstrak umum:
"Eksistensial" hanya berarti bahwa ada beberapa jenis yang mematuhi aturan yang ditetapkan di sini.
sumber