Apa penjelasan singkat tapi lengkap dari sistem tipe murni / tergantung?

32

Jika sesuatu itu sederhana, maka itu harus sepenuhnya dijelaskan dengan beberapa kata. Ini dapat dilakukan untuk kalkulus λ:

Kalkulus λ adalah tata bahasa sintaksis (pada dasarnya, struktur) dengan aturan reduksi (yang berarti prosedur pencarian / penggantian berulang kali diterapkan pada setiap kemunculan pola tertentu hingga tidak ada pola seperti itu).

Tatabahasa:

Term = (Term Term) | (λ Var . Term) | Var

Aturan pengurangan:

((λ var body) term) -> SUBS(body,var,term)
    where `SUBS` replaces all occurrences of `var`
    by `term` in `body`, avoiding name capture.

Contoh:

(λ a . a)                             -> (λ a a)
((λ a . (λ b . (b a))) (λ x . x))     -> (λ b . (b (λ x x)))
((λ a . (a a)) (λ x . x))             -> (λ x . x)
((λ a . (λ b . ((b a) a))) (λ x . x)) -> (λ b . ((b (λ x . x)) (λ x . x)))
((λ x . (x x)) (λ x . (x x)))         -> never halts

Meskipun agak informal, orang bisa berpendapat ini cukup informatif bagi manusia normal untuk memahami kalkulus λ secara keseluruhan - dan dibutuhkan 22 garis penurunan harga. Saya mencoba memahami sistem tipe murni / dependen seperti yang digunakan oleh Idris / Agda dan proyek-proyek serupa, tetapi penjelasan singkat yang bisa saya temukan adalah Simply Easy - sebuah makalah yang hebat, tetapi itu sepertinya mengasumsikan banyak pengetahuan sebelumnya (Haskell, inductive definisi) yang tidak saya miliki. Saya pikir sesuatu yang lebih singkat, kurang kaya bisa menghilangkan beberapa hambatan itu. Demikian,

Apakah mungkin untuk memberikan penjelasan singkat dan lengkap tentang sistem tipe murni / dependen, dalam format yang sama dengan yang saya sajikan pada kalkulus λ di atas?

Viktor Maia
sumber
4
Aturan Sistem Jenis Murni sangat singkat. Simply Easy adalah tentang menerapkan tipe dependen.
2
Jadi itu bukan "permusuhan" dalam arti ofensif, tetapi dalam arti Anda pikir saya banyak menuntut karena tidak menunjukkan upaya yang cukup dalam menemukan jawaban sendiri? Jika itu masalahnya, saya setuju pertanyaan ini mungkin banyak menuntut jadi mungkin itu hanya buruk. Tetapi ada juga banyak upaya di baliknya, apakah Anda pikir saya harus mengedit dalam upaya saya?
MaiaVictor
3
Saya tersinggung juga, atas nama rekan penulis saya yang menulis teks "Implementasi Tutorial dari Kalkulus Lambda yang Diketik dengan Ketergantungan", yang menggantikan "Simply Easy" sebagai judul yang berfungsi. Saya menulis kernel dari kode, yang merupakan typechecker di <100 baris Haskell.
2
Kemudian saya tentu saja mengekspresikan diri saya dengan buruk. Saya suka kertas "Simply Easy" dan saya membacanya di setiap jeda sejak beberapa hari yang lalu - itu adalah satu-satunya hal di dunia yang memberi saya sensasi parsial saya mulai memahami subjek (dan percaya saya sudah mencoba) . Tapi saya pikir itu ditujukan untuk publik dengan pengetahuan lebih dari yang saya miliki, dan itu bisa jadi mengapa saya masih kesulitan mendapatkan bagian dari itu. Tidak ada hubungannya dengan kualitas kertas, tetapi keterbatasan saya sendiri.
MaiaVictor
1
@pigworker dan kode adalah bagian favorit saya, tepatnya karena (dalam kaitannya dengan penjelasan bahasa Inggris) adalah penjelasan yang jauh lebih pendek, belum lengkap, dari keseluruhan, seperti yang saya tanyakan di sini. Apakah Anda memiliki salinan kode yang dapat saya unduh?
MaiaVictor

Jawaban:

7

Penolakan

Ini sangat informal, seperti yang Anda minta.

Tata bahasa

Dalam bahasa yang diketik secara dependen, kami memiliki pengikat di tingkat tipe serta di tingkat nilai:

Term = * | (∀ (Var : Term). Term) | (Term Term) | (λ Var. Term) | Var

Istilah yang diketik dengan baik adalah istilah dengan tipe terlampir, kami akan menulis t ∈ σatau

σ
t

untuk menunjukkan bahwa istilah tersebut tmemiliki tipe σ.

Aturan mengetik

Demi kesederhanaan, kami mengharuskan λ v. t ∈ ∀ (v : σ). τkeduanya λdan mengikat variabel yang sama ( vdalam hal ini).

Aturan:

t ∈ σ is well-formed if σ ∈ * and t is in normal form (0)

*            ∈ *                                                 (1)
∀ (v : σ). τ ∈ *             -: σ ∈ *, τ ∈ *                     (2)
λ v. t       ∈ ∀ (v : σ). τ  -: t ∈ τ                            (3)
f x          ∈ SUBS(τ, v, x) -: f ∈ ∀ (v : σ). τ, x ∈ σ          (4)
v            ∈ σ             -: v was introduced by ∀ (v : σ). τ (5)

Jadi, *adalah "tipe semua tipe" (1), bentuk tipe dari tipe (2), abstraksi lambda memiliki tipe pi (3) dan jika vdiperkenalkan oleh ∀ (v : σ). τ, maka vmemiliki tipe σ(5).

"dalam bentuk normal" berarti kami melakukan pengurangan sebanyak mungkin menggunakan aturan pengurangan:

Aturan pengurangan "The"

(λ v. b ∈ ∀ (v : σ). τ) (t ∈ σ) ~> SUBS(b, v, t) ∈ SUBS(τ, v, t)
    where `SUBS` replaces all occurrences of `v`
    by `t` in `τ` and `b`, avoiding name capture.

Atau dalam sintaks dua dimensi di mana

σ
t

berarti t ∈ σ:

(∀ (v : σ). τ) σ    SUBS(τ, v, t)
                 ~>
(λ  v     . b) t    SUBS(b, v, t)

Ini hanya mungkin untuk menerapkan abstraksi lambda ke suatu istilah ketika istilah tersebut memiliki tipe yang sama dengan variabel dalam semua kuantifier terkait. Kemudian kita mengurangi abstraksi lambda dan kuantifikasi forall dengan cara yang sama seperti pada kalkulus lambda murni sebelumnya. Setelah mengurangi bagian level nilai, kami mendapatkan (4) aturan mengetik.

Sebuah contoh

Inilah operator aplikasi fungsi:

∀ (A : *) (B : A -> *) (f : ∀ (y : A). B y) (x : A). B x
λ  A       B            f                    x     . f x

(kita menyingkat ∀ (x : σ). τke σ -> τjika τtidak menyebutkan x)

fpengembalian B yuntuk semua yjenis yang disediakan A. Kami menerapkan funtuk x, yang merupakan dari jenis yang tepat A, dan pengganti yuntuk xdi setelah ., sehingga f x ∈ SUBS(B y, y, x)~> f x ∈ B x.

Sekarang mari kita menyingkat operator aplikasi fungsi appdan menerapkannya sendiri:

∀ (A : *) (B : A -> *). ?
λ  A       B          . app ? ? (app A B)

Saya menempatkan ?persyaratan yang harus kami sediakan. Pertama-tama kami secara eksplisit memperkenalkan dan membuat instantiate Adan B:

∀ (f : ∀ (y : A). B y) (x : A). B x
app A B

Sekarang kita perlu menyatukan apa yang kita miliki

∀ (f : ∀ (y : A). B y) (x : A). B x

yang sama dengan

(∀ (y : A). B y) -> ∀ (x : A). B x

dan apa yang app ? ?diterima

∀ (x : A'). B' x

Ini menghasilkan

A' ~ ∀ (y : A). B y
B' ~ λ _. ∀ (x : A). B x -- B' ignores its argument

(lihat juga Apa itu predicativity? )

Ekspresi kami (setelah diganti nama) menjadi

∀ (A : *) (B : A -> *). ?
λ  A       B          . app (∀ (x : A). B x) (λ _. ∀ (x : A). B x) (app A B)

Karena untuk apa pun A, Bdan f(di mana f ∈ ∀ (y : A). B y)

∀ (y : A). B y
app A B f

kita bisa instantiate Adan Bmendapatkan (untuk apa pun fdengan tipe yang sesuai)

∀ (y : ∀ (x : A). B x). ∀ (x : A). B x
app (∀ (x : A). B x) (λ _. ∀ (x : A). B x) f

dan tipe tanda tangan setara dengan (∀ (x : A). B x) -> ∀ (x : A). B x.

Seluruh ekspresi itu

∀ (A : *) (B : A -> *). (∀ (x : A). B x) -> ∀ (x : A). B x
λ  A       B          . app (∀ (x : A). B x) (λ _. ∀ (x : A). B x) (app A B)

Yaitu

∀ (A : *) (B : A -> *) (f : ∀ (x : A). B x) (x : A). B x
λ  A       B            f                    x     .
    app (∀ (x : A). B x) (λ _. ∀ (x : A). B x) (app A B) f x

yang setelah semua pengurangan pada level nilai memberikan appkembali yang sama .

Jadi sementara ini hanya memerlukan beberapa langkah dalam kalkulus lambda murni untuk mendapatkan appdari app app, dalam pengaturan diketik (dan terutama ketergantungan diketik) kami juga perlu peduli tentang unifikasi dan hal-hal menjadi lebih kompleks bahkan dengan beberapa kenyamanan yang tidak konsisten ( * ∈ *).

Ketik memeriksa

  • Jika tini *kemudian t ∈ *oleh (1)
  • Jika tadalah ∀ (x : σ) τ, σ ∈? *, τ ∈? *(lihat catatan tentang ∈?bawah) kemudian t ∈ *oleh (2)
  • Jika tadalah f x, f ∈ ∀ (v : σ) τuntuk beberapa σdan τ, x ∈? σkemudian t ∈ SUBS(τ, v, x)oleh (4)
  • Jika tvariabel v, vdiperkenalkan ∀ (v : σ). τsaat itu t ∈ σoleh (5)

Ini semua adalah aturan inferensi, tetapi kami tidak dapat melakukan hal yang sama untuk lambdas (inferensi tipe tidak dapat ditentukan untuk tipe dependen). Jadi untuk lambda kita periksa ( t ∈? σ) daripada menyimpulkan:

  • Jika tini λ v. bdan diperiksa terhadap ∀ (v : σ) τ, b ∈? τmakat ∈ ∀ (v : σ) τ
  • Jika tada sesuatu yang lain dan diperiksa, σmaka simpulkan jenis tpenggunaan fungsi di atas dan periksa apakah ituσ

Pemeriksaan kesetaraan untuk tipe mengharuskan mereka dalam bentuk normal, jadi untuk memutuskan apakah tmemiliki tipe, σpertama kita periksa yang σmemiliki tipe *. Jika demikian, maka σdapat dinormalisasi (modulo Girard's paradox) dan menjadi dinormalisasi (maka σmenjadi terbentuk dengan baik oleh (0)). SUBSjuga menormalkan ekspresi untuk dipertahankan (0).

Ini disebut pengecekan jenis dua arah. Dengan itu kita tidak perlu membubuhi keterangan setiap lambda dengan jenis: jika dalam f xjenis fdiketahui, maka xdiperiksa terhadap jenis argumen yang fditerima alih-alih disimpulkan dan dibandingkan untuk kesetaraan (yang juga kurang efisien). Tetapi jika flambda, itu membutuhkan penjelasan jenis eksplisit (penjelasan dihilangkan dalam tata bahasa dan di mana-mana, Anda dapat menambahkan Ann Term Termatau λ' (σ : Term) (v : Var)ke konstruktor).

Juga, lihat Simpler, Lebih Mudah! blogpost.

pengguna3237465
sumber
1
Menekan "Lebih Sederhana, Lebih Mudah".
Aturan pengurangan pertama pada forall terlihat aneh. Tidak seperti lambdas, forall tidak harus diterapkan dengan cara yang diketik dengan baik (kan?).
@ Ya, saya tidak mengerti apa yang Anda katakan. Mungkin notasi saya buruk: aturan pengurangan mengatakan (λ v. b ∈ ∀ (v : σ). τ) (t ∈ σ)~> SUBS(b, v, t) ∈ SUBS(τ, v, t).
user3237465
1
Saya menemukan notasi itu menyesatkan. Sepertinya Anda memiliki dua aturan: satu untuk omong kosong (∀ (v : σ). τ) t ~> ...dan yang lain untuk yang bermakna (λ v. b) t ~> .... Saya akan menghapus yang pertama, dan mengubahnya menjadi komentar di bawah ini.
1
Aturan (1) berisi kesimpulannya sebagai premis. Anda dapat membandingkan kesederhanaan sistem Anda dengan versi dua arah hanya setelah Anda memiliki sistem yang berfungsi. Anda mungkin mengatakan Anda menjaga semuanya tetap normal, tetapi aturan Anda tidak.
24

Mari kita mulai. Saya tidak akan peduli dengan paradoks Girard, karena mengalihkan perhatian dari ide-ide sentral. Saya perlu memperkenalkan beberapa mesin presentasi tentang penilaian dan derivasi dan semacamnya.

Tatabahasa

Term :: = (Elim) | * | (Var: Term) → Term | λVar↦Term

Elim :: = Istilah: Istilah | Var | Istilah Elim

Tata bahasanya memiliki dua bentuk yang saling didefinisikan, "istilah" yang merupakan gagasan umum tentang benda (jenis adalah benda, nilai adalah benda), termasuk * (jenis jenis), jenis fungsi dependen, dan abstraksi lambda, tetapi juga menanamkan " eliminasi "(yaitu" penggunaan "daripada" konstruksi "), yang merupakan aplikasi bersarang di mana hal yang pada akhirnya di posisi fungsi adalah variabel atau istilah yang dijelaskan dengan tipenya.

Aturan Pengurangan

(λy↦t: (x: S) → T) s ↝ t [s: S / y]: T [s: S / x]

(t: T) ↝ t

Operasi substitusi t [e / x] menggantikan setiap kemunculan variabel x dengan eliminasi e, menghindari penangkapan nama. Untuk membentuk aplikasi yang dapat mengurangi, istilah lambda harus dianotasi berdasarkan jenisnya untuk melakukan eliminasi . Anotasi jenis ini memberikan abstraksi lambda semacam "reaktivitas", yang memungkinkan aplikasi untuk melanjutkan. Setelah kami mencapai titik di mana tidak ada lagi aplikasi yang terjadi dan t: T sedang disematkan kembali ke dalam sintaksis istilah, kita dapat menghapus anotasi jenis.

Mari kita memperluas hubungan ↝ reduksi dengan penutupan struktural: aturan berlaku di mana saja di dalam istilah dan eliminasi bahwa Anda dapat menemukan sesuatu yang cocok dengan sisi kiri. Tulis ↝ * untuk penutupan refleksif-transitif (0-atau-lebih-langkah) dari ↝. Sistem reduksi yang dihasilkan bersatu dalam pengertian ini:

Jika s ↝ * p dan s ↝ * q, maka ada beberapa r sehingga p ↝ * r dan q ↝ * r.

Konteks

Konteks :: = | Konteks, Var: Istilah

Konteks adalah urutan yang menetapkan jenis ke variabel, tumbuh di sebelah kanan, yang kita anggap sebagai "lokal", memberi tahu kita tentang variabel terikat terbaru. Properti penting dari konteks adalah bahwa selalu mungkin untuk memilih variabel yang belum digunakan dalam konteks. Kami mempertahankan invarian bahwa variabel yang dianggap sebagai tipe dalam konteks berbeda.

Penghakiman

Penghakiman :: = Konteks ⊢ Term memiliki Term | Konteks ⊢ Elim is Term

Itu tata bahasa penilaian, tetapi bagaimana cara membacanya? Sebagai permulaan, ⊢ adalah simbol "turnstile" tradisional, memisahkan asumsi dari kesimpulan: Anda dapat membacanya secara informal sebagai "berkata".

G ⊢ T memiliki t

berarti konteks yang diberikan G, tipe T mengakui istilah t;

G ⊢ e adalah S

berarti bahwa diberikan konteks G, eliminasi e diberikan tipe S.

Penilaian memiliki struktur yang menarik: nol atau lebih input , satu subjek atau lebih , nol atau lebih output .

INPUTS                   SUBJECT        OUTPUTS
Context |- Term   has    Term
Context |-               Elim      is   Term

Artinya, kita harus mengusulkan jenis-jenis istilah terlebih dahulu dan cukup memeriksanya , tetapi kita mensintesiskan jenis-jenis eliminasi.

Aturan Mengetik

Saya menyajikan ini dalam gaya Prolog yang samar-samar, menulis J -: P1; ...; Pn untuk menunjukkan bahwa penilaian J berlaku jika premis P1 hingga Pn juga berlaku. Premis akan menjadi penilaian lain, atau klaim tentang pengurangan.

Ketentuan

G ⊢ T memiliki t -: T ↝ R; G ⊢ R memiliki t

G ⊢ * memiliki *

G ⊢ * memiliki (x: S) → T -: G ⊢ * memiliki S; G, z: S! - * memiliki T [z / x]

G ⊢ (x: S) → T memiliki λy↦t -: G, z: S ⊢ T [z / x] memiliki t [z / y]

G ⊢ T memiliki (e) -: G ⊢ e adalah T

Eliminasi

G ⊢ e adalah R -: G ⊢ e adalah S; S ↝ R

G, x: S, G '⊢ x adalah S

G ⊢ fs adalah T [s: S / x] -: G ⊢ f adalah (x: S) → T; G ⊢ S memiliki s

Dan itu dia!

Hanya dua aturan yang tidak diarahkan sintaks: aturan yang mengatakan "Anda bisa mengurangi tipe sebelum Anda menggunakannya untuk memeriksa istilah" dan aturan yang mengatakan "Anda bisa mengurangi tipe setelah Anda mensintesisnya dari eliminasi". Salah satu strategi yang dapat dilakukan adalah mengurangi tipe hingga Anda mengekspos konstruktor paling atas.

Sistem ini tidak sangat normal (karena Paradox Girard, yang merupakan paradoks referensi diri pembohong), tetapi dapat dibuat sangat normal dengan memecah * menjadi "level alam semesta" di mana setiap nilai yang melibatkan tipe di level bawah sendiri memiliki tipe pada level yang lebih tinggi, mencegah referensi diri.

Sistem ini, bagaimanapun, memiliki sifat pelestarian tipe, dalam pengertian ini.

Jika G ⊢ T memiliki t dan G ↝ * D dan T ↝ * R dan t ↝ r, maka D ⊢ R memiliki r.

Jika G ⊢ e adalah S dan G ↝ * D dan e ↝ f, maka ada R sehingga S ↝ * R dan D ⊢ f adalah R.

Konteks dapat menghitung dengan membiarkan istilah yang dikandungnya menghitung. Artinya, jika penilaian sekarang valid, Anda dapat menghitung inputnya sebanyak yang Anda suka dan subjeknya satu langkah, dan kemudian dimungkinkan untuk menghitung hasilnya entah bagaimana untuk memastikan penilaian yang dihasilkan tetap valid. Buktinya adalah induksi sederhana pada derivasi mengetik, mengingat pertemuan -> *.

Tentu saja, saya hanya menyajikan inti fungsional di sini, tetapi ekstensi bisa sangat modular. Berikut ini adalah pasangan.

Term :: = ... | (x: S) * T | s, t

Elim :: = ... | e.head | e.tail

(s, t: (x: S) * T) .head ↝ s: S

(s, t: (x: S) * T) .tail ↝ t: T [s: S / x]

G ⊢ * memiliki (x: S) * T -: G ⊢ * memiliki S; G, z: S ⊢ * memiliki T [z / x]

G ⊢ (x: S) * T memiliki s, t -: G ⊢ S memiliki s; G ⊢ T [s: S / x] memiliki t

G ⊢ e.head adalah S -: G ⊢ e adalah (x: S) * T

G ⊢ e.tail adalah T [e.head / x] -: G ⊢ e is (x: S) * T

pekerja pigmen
sumber
1
G, x:S, G' ⊢ x is S -: G' ⊬ x?
user3237465
1
@ user3237465 Tidak. Terima kasih! Tetap. (Ketika saya mengganti pintu putar ascii art dengan pintu putar html (sehingga membuatnya tidak terlihat di ponsel saya; maaf kalau itu terjadi di tempat lain). Saya melewatkan yang itu.)
1
Oh, saya pikir Anda baru saja menunjukkan kesalahan ketik. Aturan mengatakan bahwa, untuk setiap variabel dalam konteks, kami mensintesis jenis yang diberikan konteks. Ketika memperkenalkan konteks, saya berkata, "Kami mempertahankan invarian bahwa variabel yang dianggap tipe dalam konteks berbeda." jadi membayangi dilarang. Anda akan melihat bahwa setiap kali aturan memperluas konteks, mereka selalu memilih "z" baru yang menjadi contoh pengikat apa pun yang kita lewati. Membayangi adalah laknat. Jika Anda memiliki konteks x: *, x: x maka jenis x yang lebih lokal tidak lagi ok karena itu x di luar cakupan.
1
Saya hanya ingin Anda dan para penjawab lainnya tahu bahwa saya akan kembali ke utas ini setiap istirahat dari pekerjaan. Saya benar-benar ingin belajar ini, dan untuk pertama kalinya saya jatuh seperti saya benar-benar mendapatkan sebagian besar. Langkah selanjutnya adalah mengimplementasikan dan menulis beberapa program. Saya senang bisa hidup di era di mana informasi tentang topik-topik luar biasa seperti itu tersedia di seluruh dunia untuk seseorang seperti saya, dan itu semua berkat para jenius seperti Anda yang mendedikasikan waktu hidupnya untuk menyebarkan pengetahuan itu, karena gratis, di internet. Maaf lagi karena mengutarakan pertanyaan saya dengan buruk, dan terima kasih!
MaiaVictor
1
@cody Ya, tidak ada ekspansi. Untuk melihat mengapa itu tidak perlu, perhatikan bahwa dua aturan perhitungan memungkinkan Anda untuk menerapkan strategi di mana Anda menormalkan jenis sepenuhnya sebelum Anda memeriksa persyaratan, dan Anda juga menormalkan jenis segera setelah mensintesis mereka dari eliminasi. Jadi dalam aturan di mana jenis harus cocok, mereka sudah dinormalisasi, maka sama di hidung jika "asli" diperiksa dan disintesis jenis dapat dikonversi. Sementara itu, membatasi pemeriksaan kesetaraan ke tempat itu hanya ok karena fakta ini: jika T dapat dikonversi menjadi tipe kanonik, itu mengurangi ke tipe kanonik.
Pigworker
8

Korespondensi Curry-Howard mengatakan bahwa ada korespondensi sistematis antara sistem tipe dan sistem bukti dalam logika. Mengambil pandangan programmer-centric ini, Anda bisa menyusunnya kembali dengan cara ini:

  • Sistem bukti logis adalah bahasa pemrograman.
  • Bahasa-bahasa ini diketik secara statis.
  • Jenis tanggung jawab sistem dalam bahasa seperti itu adalah untuk melarang program yang akan membangun bukti tidak sehat.

Terlihat dari sudut ini:

  • Kalkulus lambda yang tidak diketik yang Anda simpulkan tidak memiliki sistem tipe yang signifikan, jadi sistem bukti yang dibangun di atasnya akan tidak sehat.
  • Kalkulus lambda yang diketik sederhana adalah bahasa pemrograman yang memiliki semua jenis yang diperlukan untuk membangun bukti suara dalam logika sentensial ("jika / kemudian", "dan", "atau", "atau", "tidak"). Tetapi tipenya tidak cukup baik untuk memeriksa bukti yang melibatkan bilangan bulat ("untuk semua x, ..."; "terdapat x sedemikian rupa sehingga ...").
  • Kalkulus lambda yang diketik secara akurat memiliki tipe dan aturan yang mendukung logika sentensial dan quantifiers tingkat pertama (kuantifikasi atas nilai).

Berikut adalah aturan deduksi alami untuk logika tingkat pertama, menggunakan diagram dari entri Wikipedia tentang deduksi alami . Ini pada dasarnya adalah aturan dari kalkulus lambda mengetik minimal juga.

Pengurangan alami orde pertama

Perhatikan bahwa aturan memiliki ketentuan lambda di dalamnya. Ini dapat dibaca sebagai program yang membangun bukti dari kalimat yang diwakili oleh tipenya (atau lebih tepatnya, kami hanya mengatakan program adalah bukti ). Aturan pengurangan yang serupa yang Anda berikan dapat diterapkan pada persyaratan lambda ini.


Kenapa kita peduli dengan ini? Yah, pertama-tama, karena bukti mungkin berubah menjadi alat yang berguna dalam pemrograman, dan memiliki bahasa yang dapat bekerja dengan bukti sebagai objek kelas satu membuka banyak jalan. Misalnya, jika fungsi Anda memiliki prasyarat, alih-alih menuliskannya sebagai komentar, Anda sebenarnya dapat meminta bukti sebagai argumen.

Kedua, karena jenis mesin sistem yang diperlukan untuk menangani bilangan bulat mungkin memiliki kegunaan lain dalam konteks pemrograman. Secara khusus, bahasa yang diketik secara dependen menangani universal quantifiers ("for all x, ...") dengan menggunakan konsep yang disebut tipe fungsi dependen — fungsi di mana tipe statis hasil dapat bergantung pada nilai runtime argumen.

Untuk memberikan aplikasi yang sangat pejalan kaki ini, saya menulis kode sepanjang waktu yang harus membaca file Avro yang terdiri dari catatan dengan struktur yang seragam — semua berbagi set nama dan tipe bidang yang sama. Ini mengharuskan saya untuk:

  1. Hardcode struktur catatan dalam program sebagai tipe catatan.
    • Keuntungan: Kode lebih sederhana dan kompiler dapat menangkap kesalahan dalam kode saya
    • Kerugian: Program ini hardcoded untuk membaca file yang sesuai dengan tipe catatan.
  2. Baca skema data saat runtime, perlihatkan secara umum sebagai struktur data, dan gunakan itu untuk memproses catatan secara umum
    • Keuntungan: Program saya bukan hardcoded untuk hanya satu jenis file
    • Kekurangan: Kompilator tidak dapat menangkap banyak kesalahan.

Seperti yang dapat Anda lihat di halaman tutorial Avro Java , mereka menunjukkan kepada Anda bagaimana menggunakan perpustakaan sesuai dengan kedua pendekatan ini.

Dengan tipe fungsi dependen Anda dapat memiliki kue dan memakannya, dengan biaya sistem tipe yang lebih kompleks. Anda bisa menulis fungsi yang membaca file Avro, mengekstrak skema, dan mengembalikan konten file sebagai aliran catatan yang jenis statis tergantung pada skema yang disimpan dalam file . Kompiler akan dapat menangkap kesalahan di mana saya, misalnya, mencoba mengakses bidang bernama yang mungkin tidak ada dalam catatan file yang akan diproses saat runtime. Manis ya

sakundim
sumber
1
Tipe bangunan saat runtime dalam mode yang Anda sebutkan adalah sesuatu yang sangat keren yang belum saya pikirkan. Agak manis, memang! Terima kasih atas jawaban mendalamnya.
MaiaVictor