Masalah yang merepresentasikan variabel terikat dalam sintaksis, dan khususnya substitusi yang menghindari penangkapan, telah diketahui dan memiliki sejumlah solusi: variabel bernama dengan kesetaraan alfa, indeks de Bruijn, tanpa nama lokal, set nominal, dll.
Tetapi tampaknya ada pendekatan lain yang cukup jelas, yang saya lihat belum pernah digunakan di mana pun. Yaitu, dalam sintaksis dasar kita hanya memiliki satu istilah "variabel", ditulis katakan , dan kemudian secara terpisah kita memberikan fungsi yang memetakan setiap variabel ke pengikat yang cakupannya terletak. Jadi seperti λ -term
akan ditulis , dan fungsinya akan memetakan pertama ke pertama dan kedua ke kedua . Jadi itu semacam indeks de Bruijn, hanya alih-alih harus "menghitung λ s" saat Anda kembali keluar dari istilah untuk menemukan binder yang sesuai, Anda hanya mengevaluasi suatu fungsi. (Jika menyatakan ini sebagai struktur data dalam implementasi, saya akan berpikir untuk melengkapi setiap objek variabel-istilah dengan pointer / referensi sederhana ke objek istilah pengikat yang sesuai.)
Jelas ini tidak masuk akal untuk menulis sintaks pada halaman untuk dibaca manusia, tetapi tidak ada indeks de Bruijn. Sepertinya bagi saya itu masuk akal secara matematis, dan khususnya itu membuat penggantian-hindarkan sangat mudah: cukup masukkan istilah yang Anda gantikan dan gunakan penyatuan fungsi pengikatan. Memang benar bahwa itu tidak memiliki gagasan "variabel bebas", tetapi kemudian (lagi) tidak melakukan indeks de Bruijn benar-benar; dalam kedua kasus istilah yang berisi variabel bebas diwakili istilah dengan daftar "konteks" pengikat di depan.
Apakah saya kehilangan sesuatu dan ada alasan mengapa representasi ini tidak berfungsi? Adakah masalah yang membuatnya jauh lebih buruk daripada yang lain sehingga tidak layak dipertimbangkan? (Satu-satunya masalah yang dapat saya pikirkan saat ini adalah bahwa himpunan istilah (bersama dengan fungsi pengikatannya) tidak didefinisikan secara induktif, tetapi itu tampaknya tidak dapat diatasi.) Atau apakah sebenarnya ada tempat di mana ia telah digunakan?
sumber
Jawaban:
Jawaban Andrej dan Łukasz merupakan poin yang bagus, tetapi saya ingin menambahkan komentar tambahan.
Untuk menggemakan apa yang dikatakan Damiano, cara ini untuk mewakili pengikat menggunakan pointer adalah yang disarankan oleh jaring-bukti, tetapi tempat paling awal di mana saya melihatnya untuk istilah lambda adalah dalam esai lama oleh Knuth:
Pada halaman 234, ia menggambar diagram berikut (yang ia sebut "struktur informasi") yang mewakili istilah :( λ y. λ z. yz) x
Representasi grafis dari istilah lambda ini juga dipelajari secara independen (dan lebih dalam) dalam dua tesis pada awal 1970-an, baik oleh Christopher Wadsworth (1971, Semantik dan Pragmatik dari Lambda-Calculus ) dan oleh Richard Statman (1974, Structural Complexity) dari Bukti ). Saat ini, diagram seperti itu sering disebut sebagai "λ-grafik" (lihat contoh makalah ini ).
Perhatikan bahwa istilah dalam diagram Knuth adalah linier , dalam arti bahwa setiap variabel bebas atau terikat terjadi tepat satu kali - seperti yang telah disebutkan orang lain, ada masalah dan pilihan non-sepele yang dibuat untuk mencoba memperluas representasi semacam ini ke non istilah -linier.
sumber
Saya tidak yakin bagaimana fungsi variabel-ke-pengikat Anda akan diwakili dan untuk tujuan apa Anda ingin menggunakannya. Jika Anda menggunakan back-pointer maka seperti yang dicatat Andrej kompleksitas komputasi substitusi tidak lebih baik dari penggantian nama alpha klasik.
Dari komentar Anda pada jawaban Andrej, saya menyimpulkan bahwa sampai batas tertentu Anda tertarik untuk berbagi. Saya dapat memberikan beberapa masukan di sini.
Dalam kalkulus lambda yang diketik khas, melemah dan kontraksi, bertentangan dengan aturan lain, tidak memiliki sintaks.
Mari kita tambahkan beberapa sintaks:
Dengan sintaks itu, setiap variabel digunakan tepat dua kali, sekali di mana ia terikat dan sekali di mana ia digunakan. Ini memungkinkan kita untuk menjauhkan diri dari sintaksis tertentu dan melihat istilah tersebut sebagai grafik di mana variabel dan istilahnya bertepi.
Dari kompleksitas algoritmik, kita sekarang dapat menggunakan pointer bukan dari variabel ke binder, tetapi dari binder ke variabel dan memiliki substitusi dalam waktu yang konstan.
Selain itu, reformulasi ini memungkinkan kita melacak penghapusan, penyalinan, dan berbagi dengan lebih setia. Seseorang dapat menulis aturan yang secara bertahap menyalin (atau menghapus) suatu istilah saat berbagi subterms. Ada banyak cara untuk melakukan itu. Dalam beberapa pengaturan terbatas , kemenangannya cukup mengejutkan .
Ini semakin dekat dengan topik jaring interaksi, kombinator interaksi, substitusi eksplisit, logika linier, evaluasi optimal Lamping, berbagi grafik, logika ringan dan lainnya.
Semua topik ini sangat menarik bagi saya dan saya dengan senang hati memberikan referensi yang lebih spesifik, tetapi saya tidak yakin apakah semua ini bermanfaat bagi Anda dan apa minat Anda.
sumber
Struktur data Anda berfungsi tetapi tidak akan lebih efisien daripada pendekatan lain karena Anda perlu menyalin setiap argumen pada setiap pengurangan beta, dan Anda harus membuat salinan sebanyak mungkin karena ada kemunculan variabel terikat. Dengan cara ini Anda terus menghancurkan pembagian memori antara subterma. Dikombinasikan dengan fakta bahwa Anda mengusulkan solusi non-murni yang melibatkan manipulasi pointer, dan karenanya sangat rawan kesalahan, mungkin tidak sepadan dengan masalahnya.
Tapi saya akan senang melihat percobaan! Anda dapat mengambil
lambda
dan mengimplementasikannya dengan struktur data Anda (OCaml memiliki pointer, mereka disebut referensi ). Lebih atau kurang, Anda hanya perlu menggantisyntax.ml
dannorm.ml
dengan versi Anda. Itu kurang dari 150 baris kode.sumber
Jawaban lain sebagian besar membahas masalah implementasi. Karena Anda menyebutkan motivasi utama Anda melakukan pembuktian matematis tanpa terlalu banyak pembukuan, berikut adalah masalah utama yang saya lihat.
Ketika Anda mengatakan "fungsi yang memetakan setiap variabel ke pengikat yang memiliki cakupannya": tipe output dari fungsi ini sedikit lebih halus daripada yang membuatnya terdengar! Secara khusus, fungsi harus mengambil nilai dalam sesuatu seperti "pengikat istilah yang sedang dipertimbangkan" - yaitu beberapa set yang bervariasi tergantung pada istilah (dan jelas bukan subset dari set ambient yang lebih besar dengan cara yang bermanfaat). Jadi dalam substitusi, Anda tidak bisa hanya "mengambil penyatuan fungsi pengikatan": Anda juga harus mengindeks ulang nilai-nilai mereka, menurut beberapa peta dari binder dalam istilah asli ke binder dalam hasil substitusi.
Pengindeksan ulang ini tentunya harus "rutin", dalam arti bahwa mereka dapat disapu dengan baik di bawah karpet, atau dikemas dengan baik dalam hal semacam fungsi atau kealamian. Tetapi hal yang sama berlaku untuk pembukuan yang terlibat dalam bekerja dengan variabel bernama. Jadi secara keseluruhan, tampaknya bagi saya bahwa akan ada paling sedikit pembukuan yang terlibat dengan pendekatan ini dibandingkan dengan pendekatan yang lebih standar.
Selain itu, ini adalah pendekatan yang sangat menarik secara konseptual, dan saya ingin melihatnya bekerja dengan hati-hati - saya bisa membayangkan itu mungkin memberikan cahaya yang berbeda pada beberapa aspek sintaksis daripada pendekatan standar lakukan.
sumber
Lazy.t
Secara keseluruhan, saya pikir itu adalah representasi yang keren, tetapi melibatkan beberapa pembukuan dengan pointer, untuk menghindari putusnya tautan yang mengikat. Itu mungkin untuk mengubah kode untuk menggunakan bidang yang bisa berubah saya kira, tetapi pengkodean dalam Coq kemudian akan menjadi kurang langsung. Saya masih yakin bahwa ini sangat mirip dengan HOAS, walaupun struktur pointer dibuat eksplisit. Namun, keberadaan
Lazy.t
menyiratkan bahwa beberapa kode mungkin dievaluasi pada waktu yang salah. Ini bukan kasus dalam kode saya karena hanya penggantian variabel dengan variabel yang dapat terjadi padaforce
waktu (dan bukan evaluasi misalnya).sumber