Apakah varian Lisp lengkap yang diketik secara statis dimungkinkan?

107

Apakah varian Lisp lengkap yang diketik secara statis dimungkinkan? Apakah masuk akal jika sesuatu seperti ini ada? Saya percaya salah satu keutamaan bahasa Lisp adalah kesederhanaan definisinya. Apakah pengetikan statis akan mengganggu prinsip inti ini?

Lambda yang Kedua dari Belakang
sumber
10
Saya suka makro bentuk bebas Lisp, tapi saya suka kekokohan sistem tipe Haskell. Saya ingin melihat seperti apa Lisp yang diketik secara statis.
mcandre
4
Pertanyaan bagus! Saya percaya shenlanguage.org melakukan itu. Saya berharap ini menjadi lebih umum.
Hamish Grubijan
Bagaimana Anda melakukan komputasi simbolik dengan Haskell? (selesaikan 'x' (= (+ xy) (* xy))). Jika Anda meletakkannya dalam sebuah string tidak ada pemeriksaan (tidak seperti Lisp yang dapat menggunakan makro untuk menambahkan pemeriksaan). Jika Anda menggunakan tipe data aljabar atau daftar ... Ini akan sangat bertele-tele: selesaikan (Sym "x") (Persamaan (Plus (Sym "x") (Sym "y")) (Mult (Sym "x") (Sym "y")))
aoeu256

Jawaban:

57

Ya, itu sangat mungkin, meskipun sistem tipe HM-style standar biasanya merupakan pilihan yang salah untuk sebagian besar kode Lisp / Skema idiomatik. Lihat Typed Racket untuk bahasa terkini yaitu "Full Lisp" (lebih mirip Scheme, sebenarnya) dengan pengetikan statis.

Eli Barzilay
sumber
1
Masalahnya di sini adalah, apa jenis daftar yang membentuk seluruh kode sumber program raket yang diketik?
Zorf
18
Biasanya begitu Sexpr.
Eli Barzilay
Tapi kemudian, saya bisa menulis coerce :: a->bdalam istilah eval. Dimana jenis keamanannya?
ssice
2
@ssice: ketika Anda menggunakan fungsi evalyang tidak diketik seperti Anda perlu menguji hasilnya untuk melihat apa yang keluar, yang bukan hal baru di Typed Racked (kesepakatan yang sama sebagai fungsi yang mengambil tipe gabungan Stringdan Number). Cara implisit untuk melihat bahwa hal ini dapat dilakukan adalah fakta bahwa Anda dapat menulis dan menggunakan bahasa yang diketik secara dinamis dalam bahasa yang diketik secara statis-HM.
Eli Barzilay
37

Jika yang Anda inginkan hanyalah bahasa yang diketik secara statis yang tampak seperti Lisp, Anda dapat melakukannya dengan mudah, dengan menentukan pohon sintaks abstrak yang mewakili bahasa Anda dan kemudian memetakan AST tersebut ke ekspresi-S. Namun, saya tidak berpikir saya akan menyebut hasilnya Lisp.

Jika Anda menginginkan sesuatu yang benar-benar memiliki karakteristik Lisp-y selain sintaks, dimungkinkan untuk melakukan ini dengan bahasa yang diketik secara statis. Namun, ada banyak karakteristik untuk Lisp yang sulit untuk mendapatkan banyak pengetikan statis berguna. Untuk mengilustrasikannya, mari kita lihat struktur daftar itu sendiri, yang disebut kontra , yang merupakan blok penyusun utama Lisp.

Menyebut kontra daftar, meskipun (1 2 3)terlihat seperti satu, adalah sedikit keliru. Misalnya, ini sama sekali tidak dapat dibandingkan dengan daftar yang diketik secara statis, seperti daftar C ++ std::listatau Haskell. Itu adalah daftar tertaut satu dimensi yang semua selnya berjenis sama. Lisp dengan senang hati mengizinkan (1 "abc" #\d 'foo). Plus, bahkan jika Anda memperluas daftar yang diketik statis untuk mencakup daftar-daftar, tipe objek ini mengharuskan setiap elemen dari daftar adalah sub-daftar. Bagaimana Anda mewakili ((1 2) 3 4)mereka?

Lisp kerucut membentuk pohon biner, dengan daun (atom) dan cabang (kerucut). Selanjutnya, daun dari pohon seperti itu mungkin mengandung semua jenis Lisp atomik (non-kontra)! Fleksibilitas struktur inilah yang membuat Lisp begitu baik dalam menangani komputasi simbolik, AST, dan mengubah kode Lisp itu sendiri!

Jadi bagaimana Anda memodelkan struktur seperti itu dalam bahasa yang diketik secara statis? Mari kita coba di Haskell, yang memiliki sistem tipe statis yang sangat kuat dan tepat:

type Symbol = String
data Atom = ASymbol Symbol | AInt Int | AString String | Nil
data Cons = CCons Cons Cons 
            | CAtom Atom

Masalah pertama Anda adalah cakupan tipe Atom. Jelas, kami belum memilih jenis Atom dengan fleksibilitas yang cukup untuk mencakup semua jenis objek yang ingin kami selempang secara conses. Alih-alih mencoba memperluas struktur data Atom seperti yang tercantum di atas (yang dapat Anda lihat dengan jelas adalah rapuh), katakanlah kami memiliki kelas tipe magis Atomicyang membedakan semua tipe yang kami ingin buat menjadi atom. Kemudian kami dapat mencoba:

class Atomic a where ?????
data Atomic a => Cons a = CCons Cons Cons 
                          | CAtom a

Tetapi ini tidak akan berhasil karena membutuhkan semua atom di pohon memiliki tipe yang sama . Kami ingin mereka dapat berbeda dari daun ke daun. Pendekatan yang lebih baik membutuhkan penggunaan bilangan eksistensial Haskell :

class Atomic a where ?????
data Cons = CCons Cons Cons 
            | forall a. Atomic a => CAtom a 

Tapi sekarang Anda sampai pada inti masalahnya. Apa yang dapat Anda lakukan dengan atom dalam struktur seperti ini? Struktur apa yang mereka miliki yang dapat dimodelkan Atomic a? Tingkat keamanan tipe apa yang Anda jamin dengan tipe seperti itu? Perhatikan bahwa kami belum menambahkan fungsi apa pun ke kelas tipe kami, dan ada alasan bagus: atom tidak memiliki kesamaan di Lisp. Supertipe mereka di Lisp hanya disebut t(yaitu top).

Untuk menggunakannya, Anda harus menemukan mekanisme untuk secara dinamis memaksa nilai atom menjadi sesuatu yang benar-benar dapat Anda gunakan. Dan pada saat itu, pada dasarnya Anda telah menerapkan subsistem yang diketik secara dinamis dalam bahasa yang Anda ketik secara statis! (Seseorang tidak bisa tidak mencatat kemungkinan akibat wajar dari Aturan Pemrograman Kesepuluh Greenspun .)

Perhatikan bahwa Haskell menyediakan dukungan hanya untuk subsistem dinamis dengan Objtipe, yang digunakan bersama dengan Dynamictipe dan kelas Typeable untuk menggantikan Atomickelas kita , yang memungkinkan nilai arbitrer disimpan dengan tipenya, dan paksaan eksplisit kembali dari tipe tersebut. Itulah jenis sistem yang perlu Anda gunakan untuk bekerja dengan struktur kontra Lisp secara umum penuh.

Yang juga dapat Anda lakukan adalah pergi ke arah lain, dan menanamkan subsistem yang diketik secara statis dalam bahasa yang diketik secara dinamis. Ini memungkinkan Anda memanfaatkan pemeriksaan tipe statis untuk bagian-bagian program Anda yang dapat memanfaatkan persyaratan tipe yang lebih ketat. Ini tampaknya menjadi pendekatan yang diambil dalam bentuk terbatas CMUCL dari pemeriksaan jenis yang tepat , misalnya.

Terakhir, ada kemungkinan memiliki dua subsistem terpisah, yang diketik secara dinamis dan statis, yang menggunakan pemrograman gaya kontrak untuk membantu menavigasi transisi di antara keduanya. Dengan cara itu bahasa dapat mengakomodasi penggunaan Lisp di mana pemeriksaan tipe statis akan lebih menjadi penghalang daripada bantuan, serta penggunaan di mana pemeriksaan tipe statis akan menguntungkan. Ini adalah pendekatan yang diambil oleh Typed Racket , seperti yang akan Anda lihat dari komentar berikut.

Owen S.
sumber
16
Jawaban ini mengalami masalah mendasar: Anda mengasumsikan bahwa sistem tipe statis harus bergaya HM. Konsep dasar yang tidak dapat diekspresikan di sana, dan merupakan fitur penting dari kode Lisp, adalah subtipe. Jika Anda akan melihat raket yang diketik, Anda akan melihat bahwa ia dapat dengan mudah mengekspresikan semua jenis daftar - termasuk hal-hal seperti (Listof Integer)dan (Listof Any). Jelas, Anda akan mencurigai yang terakhir tidak berguna karena Anda tidak tahu apa-apa tentang tipenya, tetapi di TR, Anda nanti dapat menggunakan (if (integer? x) ...)dan sistem akan tahu bahwa itu xadalah Integer di cabang pertama.
Eli Barzilay
5
Oh, dan itu adalah karakterisasi raket yang diketik buruk (yang berbeda dari sistem tipe tidak sehat yang Anda temukan di beberapa tempat). Diketik Racket adalah sebuah statis diketik bahasa, tanpa runtime overhead untuk kode diketik. Racket masih memungkinkan penulisan beberapa kode dalam TR dan beberapa dalam bahasa biasa yang tidak diketik - dan untuk kasus ini kontrak (pemeriksaan dinamis) digunakan untuk menjaga kode yang diketik dari kode yang tidak diketik yang berpotensi berperilaku buruk.
Eli Barzilay
1
@Eli Barzilay: Saya berbohong, ada empat bagian: 4. Sangat menarik bagi saya bagaimana gaya pengkodean C ++ yang diterima industri secara bertahap beralih dari subtipe ke generik. Kelemahannya adalah bahasanya tidak menyediakan bantuan untuk mendeklarasikan antarmuka yang akan digunakan fungsi generik, kelas tipe sesuatu pasti bisa membantu. Plus, C ++ 0x mungkin menambahkan inferensi tipe. Bukan HM, kurasa, tapi merayap ke arah itu?
Owen S.
1
Owen: (1) poin utamanya adalah Anda memerlukan subtipe untuk mengekspresikan jenis lispers kode yang ditulis - dan Anda tidak dapat memilikinya dengan sistem HM, jadi Anda terpaksa menggunakan tipe dan konstruktor khusus untuk setiap penggunaan, yang mana membuat semuanya jauh lebih canggung untuk digunakan. Dalam raket yang diketik menggunakan sistem dengan subtipe adalah akibat wajar dari keputusan desain yang disengaja: bahwa hasilnya harus dapat mengekspresikan jenis kode tersebut tanpa mengubah kode atau membuat jenis kustom.
Eli Barzilay
1
(2) Ya, dynamictipe menjadi populer dalam bahasa statis sebagai semacam solusi untuk mendapatkan beberapa manfaat dari bahasa yang diketik secara dinamis, dengan pertukaran biasa dari nilai-nilai ini dibungkus dengan cara yang membuat tipe dapat diidentifikasi. Tapi di sini raket yang terlalu diketik melakukan pekerjaan yang sangat baik dalam membuatnya nyaman dalam bahasa - pemeriksa tipe menggunakan kemunculan predikat untuk mengetahui lebih banyak tentang jenis. Misalnya, lihat contoh yang diketik di halaman raket dan lihat bagaimana string?"mengurangi" daftar string dan angka menjadi daftar string.
Eli Barzilay
10

Jawaban saya, tanpa tingkat kepercayaan yang tinggi mungkin . Jika Anda melihat bahasa seperti SML, misalnya, dan membandingkannya dengan Lisp, inti fungsional masing-masing hampir identik. Akibatnya, Anda tidak akan kesulitan menerapkan beberapa jenis pengetikan statis ke inti Lisp (aplikasi fungsi dan nilai primitif).

Pertanyaan Anda memang mengatakan penuh , dan di mana saya melihat beberapa masalah masuk adalah pendekatan kode-sebagai-data. Jenis ada pada tingkat yang lebih abstrak daripada ekspresi. Lisp tidak memiliki perbedaan ini - semuanya berstruktur "datar". Jika kita menganggap beberapa ekspresi E: T (di mana T adalah representasi dari tipenya), dan kemudian kita menganggap ekspresi ini sebagai data biasa, lalu apa sebenarnya tipe T di sini? Yah, itu sejenis! A kind adalah yang lebih tinggi, tipe pesanan, jadi mari kita lanjutkan dan katakan sesuatu tentang itu di kode kita:

E : T :: K

Anda mungkin melihat ke mana saya pergi dengan ini. Saya yakin dengan memisahkan informasi jenis dari kode itu akan mungkin untuk menghindari jenis referensi diri jenis ini, namun itu akan membuat jenis tidak terlalu "cadel" dalam rasa mereka. Mungkin ada banyak cara untuk mengatasi ini, meskipun tidak jelas bagi saya mana yang terbaik.

EDIT: Oh, jadi dengan sedikit googling, saya menemukan Qi , yang tampaknya sangat mirip dengan Lisp kecuali bahwa itu diketik secara statis. Mungkin ini tempat yang baik untuk mulai melihat di mana mereka membuat perubahan untuk mendapatkan pengetikan statis di sana.

Gian
sumber
Tampaknya iterasi berikutnya setelah Qi adalah Shen , dikembangkan oleh orang yang sama.
Diagon
4

Dylan: Memperluas sistem tipe Dylan untuk inferensi tipe dan deteksi kesalahan yang lebih baik

Rainer Joswig
sumber
Tautannya sudah mati. Tapi bagaimanapun juga, Dylan tidak diketik secara statis.
Björn Lindqvist
@ BjörnLindqvist: tautan itu ke tesis tentang menambahkan pengetikan bertahap ke Dylan.
Rainer Joswig
1
@ BjörnLindqvist: Saya menautkan ke makalah ikhtisar.
Rainer Joswig
Namun pengetikan bertahap tidak dihitung sebagai pengetikan statis. Jika ya, maka Pypy akan diketik secara statis karena Python juga menggunakan pengetikan bertahap.
Björn Lindqvist
2
@ BjörnLindqvist: jika kita menambahkan tipe statis melalui pengetikan bertahap dan ini diperiksa selama kompilasi, maka ini adalah pengetikan statis. Bukan saja seluruh program diketik secara statis, tetapi bagian / wilayah. homes.sice.indiana.edu/jsiek/what-is-gradual-typing ' Pengetikan bertahap adalah sistem jenis yang saya kembangkan bersama Walid Taha pada tahun 2006 yang memungkinkan bagian program diketik secara dinamis dan bagian lain diketik secara statis.'
Rainer Joswig