Beberapa waktu yang lalu (sekitar tiga tahun) saya memutuskan untuk membaca buku teks tentang Lisp. Tanpa tujuan khusus, hanya demi perkembangan umum dan kemampuan untuk mengejutkan lawan bicara dengan eksotis (begitu tampaknya, itu bahkan berhasil).
Tetapi setelah diperiksa lebih dekat, Lisp ternyata sangat kuat, fleksibel dan, anehnya, berguna dalam kehidupan sehari-hari. Semua tugas otomatisasi kecil dengan cepat dimigrasikan ke skrip di Lisp, dan ada juga peluang untuk otomatisasi yang lebih kompleks.
Perlu dicatat di sini bahwa dengan "kemampuan otomatisasi" yang saya maksudkan adalah situasi di mana total waktu untuk menulis dan men-debug suatu program kurang dari waktu yang dihabiskan secara manual untuk menyelesaikan tugas yang sama.
Paul Graham telah menulis lebih dari satu artikel dan bahkan sebuah buku tentang manfaat Lisp. Pada saat penulisan ini, Lisp berada di peringkat ke-33 di peringkat TOIBE (Delphi tiga kali mati daripada mati). Muncul pertanyaan: mengapa bahasanya sangat kecil jika nyaman? Sekitar dua tahun penggunaan memberi beberapa petunjuk alasannya.
Kekurangan
1. Struktur data bersamaSebuah konsep yang memungkinkan Anda untuk mengoptimalkan program-program fungsional, tetapi penuh dengan kesalahan-kesalahan halus secara imperatif. Kemungkinan kerusakan yang tidak disengaja pada struktur data asing ketika suatu variabel dimodifikasi yang tidak memiliki koneksi yang terlihat dengan struktur mengharuskan programmer untuk terus memantau apa yang terjadi di belakang layar dan untuk mengetahui implementasi internal dari setiap fungsi yang digunakan (baik sistem dan pengguna). Hal yang paling menakjubkan adalah kemampuan untuk merusak tubuh dari suatu fungsi dengan memodifikasi nilai baliknya.
2. Kurangnya enkapsulasiMeskipun konsep paket ada, itu tidak ada hubungannya dengan
paket di Ada atau
unit di Delphi. Kode apa pun dapat menambahkan apa pun ke paket apa pun (kecuali yang sistem). Kode apa pun dapat mengekstraksi apa pun dari paket apa pun menggunakan operator
:: .
3. Singkatan serampanganApa perbedaan antara MAPCAN dan MAPCON? Mengapa di SETQ, huruf terakhir Q? Mengingat usia bahasanya, Anda dapat memahami alasan dari keadaan ini, tetapi saya ingin sedikit bahasa yang lebih bersih.
4. MultithreadingKelemahan ini secara tidak langsung terkait dengan Lisp dan terutama menyangkut implementasi yang saya gunakan - SteelBank Common Lisp. Lisp umum tidak mendukung multithreading. Upaya untuk menggunakan implementasi yang disediakan oleh SBCL gagal.
Sangat disayangkan untuk menolak alat yang nyaman seperti itu, tetapi ketidakpuasan secara bertahap menumpuk.
Cari solusinya
Pertama, Anda bisa pergi ke Wikipedia pada halaman Lisp. Periksa bagian "Dialek". Baca pengantar singkat untuk masing-masing. Dan sadari bahwa rasa dan warna semua marker berbeda.
Jika Anda ingin melakukan sesuatu, Anda harus melakukannya sendiri
- Jean Baptiste Emmanuel Sorg
Mari kita coba membuat Lisp kita sendiri dengan menambahkan Ada, banyak Delphi dan setetes Oberon ke sana. Kami menyebutnya campuran yang dihasilkan Fox.
Konsep dasar
1. Tidak ada petunjukDalam perang melawan MASALAH-1, semua operasi harus dilakukan dengan menyalin nilai-nilai. Berdasarkan jenis struktur data dalam kode atau saat mencetak, semua propertinya, koneksi eksternal dan internal harus sepenuhnya terlihat.
2. Tambahkan modulSebagai bagian dari perjuangan melawan masalah-2, kami mengimpor
paket ,
dengan dan
menggunakan pernyataan dari Ada. Dalam prosesnya, kami membuang skema impor / bayangan yang terlalu rumit untuk simbol Lisp.
(package - ( ) () ())
(with -)
(use -)
3. Kurang singkatanKarakter yang paling umum dan umum masih akan disingkat, tetapi sebagian besar yang paling jelas:
const ,
var . Fungsi output yang diformat - FMT membutuhkan reduksi, karena sering ditemukan di dalam ekspresi.
Elt - mengambil elemen - bocor dari Common Lisp dan mengambil root, meskipun tidak perlu menguranginya.
4. pengidentifikasi kasus-sensitifSaya percaya bahwa bahasa (dan sistem file) yang benar {$ HOLYWAR +} harus case-insensitive {$ HOLYWAR-} agar tidak memutar otaknya sekali lagi.
5. Kemudahan penggunaan dengan tata letak keyboard RusiaSintaksis Lisi dengan segala cara yang mungkin menghindari penggunaan karakter yang tidak tersedia di salah satu tata letak. Tidak ada kawat gigi bujur sangkar atau keriting. Tidak #, ~, &, <,>, |. Saat membaca literal angka, koma dan titik dianggap sebagai pemisah desimal.
6. Alfabet yang diperluasSalah satu hal baik tentang SBCL adalah kode UTF-8. Kemampuan untuk mendeklarasikan konstanta BEAR, VODKA dan BALALAYKA sangat menyederhanakan penulisan kode aplikasi. Kemampuan untuk memasukkan Ω, Ψ, dan Σ membuat rumus dalam kode lebih visual. Meskipun secara teori ada kemungkinan menggunakan karakter Unicode, sulit untuk menjamin kebenaran bekerja dengan mereka (agak malas daripada sulit). Kami membatasi diri pada bahasa Sirilik, Latin, dan Yunani.
7. Numerik literalIni adalah ekstensi bahasa yang paling berguna bagi saya.
10_000
Pilihan yang terakhir bagi saya tampaknya yang paling tidak estetika, tetapi itu yang paling populer.
8. SiklusSiklus di Lisp adalah non-standar dan sangat berantakan. Sederhanakan ke set standar minimum.
(for i 5
Variabel loop tidak terlihat di luar.
(while )
9. GOTOBukan operator yang sangat diperlukan, tetapi tanpanya sulit untuk menunjukkan pengabaian aturan pemrograman struktural.
(block : (goto :))
10. Penyatuan ruang lingkupAda dua jenis ruang lingkup di Lisp: TOPLEVEL dan lokal. Dengan demikian, ada dua cara berbeda untuk mendeklarasikan variabel.
(defvar A 1) (let ((a 1)) …)
Di Fox hanya ada satu metode yang digunakan baik di tingkat atas skrip dan di area lokal, termasuk paket.
(var A 1)
Jika Anda ingin membatasi ruang lingkup, gunakan operator
(block (var A 1) (set A 2) (fmt nil A))
Tubuh loop terkandung dalam pernyataan BLOCK implisit (seperti tubuh fungsi / prosedur). Semua variabel yang dideklarasikan dalam loop dihancurkan pada akhir iterasi.
11. Karakter slot tunggalDalam Lisp, fungsi adalah objek khusus dan disimpan dalam slot simbol khusus. Satu karakter dapat secara bersamaan menyimpan variabel, fungsi, dan daftar properti. Dalam rubah, setiap karakter dikaitkan hanya dengan satu makna.
12. ELT yang nyamanAkses khas ke elemen struktur kompleks di Lisp terlihat seperti ini
(elt (slot-value (elt 1) '-2) 3)
Fox mengimplementasikan operator ELT terpadu yang menyediakan akses ke elemen tipe komposit apa pun (daftar, string, catatan, array byte, tabel hash).
(elt 1 \-2 3)
Fungsi identik juga dapat diperoleh dengan makro di Lisp
(defmacro field (object &rest f) " . (field *object* 0 :keyword symbol \"string\") . plist. ( ) . ." (if f (symbol-macrolet ((f0 (elt f 0))(rest (subseq f 1))) (cond ((numberp f0) `(field (elt ,object ,f0) ,@rest)) ((keywordp f0) `(field (getf ,object ,f0) ,@rest)) ((stringp f0) `(field (cdr (assoc ,f0 ,object :test 'equal)) ,@rest)) ((and (listp f0) (= 2 (length f0))) `(field (,(car f0) ,(cadr f0) ,object) ,@rest)) ((symbolp f0) `(field (,f0 ,object) ,@rest)) (t `(error " ")))) object))
13. Pembatasan mode transfer parameter subrutinSetidaknya ada lima mode transfer parameter di Lisp: wajib,
& opsional ,
& sisanya ,
& kunci ,
& keseluruhan dan kombinasi sewenang-wenang mereka diizinkan. Bahkan, sebagian besar kombinasi memberikan efek aneh.
Di Fox, hanya diperbolehkan menggunakan kombinasi parameter yang diperlukan dan salah satu mode berikut untuk dipilih
: kunci ,: opsional ,: bendera ,: istirahat .
14. MultithreadingUntuk menyederhanakan penulisan program multithreaded sepenuhnya, konsep pemisahan memori diadopsi. Ketika sebuah utas muncul, semua variabel yang tersedia untuk utas baru disalin. Semua referensi untuk variabel-variabel ini digantikan oleh referensi untuk salinan. Transfer informasi antar aliran hanya dimungkinkan melalui objek yang dilindungi atau melalui hasil yang dikembalikan oleh aliran setelah selesai.
Benda-benda yang dilindungi selalu mengandung bagian-bagian penting untuk memastikan operasi atom. Masuk ke bagian kritis adalah otomatis - tidak ada operator terpisah untuk ini dalam bahasa. Objek yang dilindungi meliputi: antrian pesan, konsol, dan deskriptor file.
Membuat utas dimungkinkan dengan fungsi tampilan multi-utas
(map-th (function (x) …) --)
Map-th secara otomatis memulai jumlah utas sama dengan jumlah prosesor dalam sistem (atau dua kali lebih banyak jika Anda memiliki Intel di dalam). Dalam panggilan rekursif, panggilan peta-th berikutnya bekerja dalam satu utas.
Selain itu, ada fungsi utas bawaan yang menjalankan prosedur / fungsi pada utas terpisah.
15. Kebersihan fungsional dalam kode imperatifFox memiliki fungsi untuk pemrograman fungsional dan prosedur prosedural. Rutinitas yang dinyatakan menggunakan kata kunci fungsi tunduk pada persyaratan tidak adanya efek samping dan independensi hasil dari faktor eksternal.
Belum direalisasi
Beberapa fitur menarik dari Lisp tetap tidak terpenuhi karena prioritas rendah.
1. Metode umumKemampuan untuk membebani fungsi dengan defgeneric / defmethod.
2. Warisan3. debugger bawaanKetika pengecualian terjadi, interpreter Lisp beralih ke mode debug.
4. UFFIAntarmuka untuk menghubungkan modul yang ditulis dalam bahasa lain.
5. BIGNUMDukungan kedalaman bit sewenang-wenang
DibuangBeberapa fitur Lisp telah dianggap dan dianggap tidak berguna / berbahaya.
1. Kombinasi metode yang dipanduKetika suatu metode dipanggil untuk suatu kelas, kombinasi dari metode induk dilakukan dan dimungkinkan untuk mengubah aturan kombinasi. Perilaku akhir dari metode ini tampaknya tidak dapat diprediksi.
2. Mulai ulangPenangan pengecualian dapat membuat perubahan pada status program dan mengirim perintah restart ke kode yang menghasilkan pengecualian. Efek dari aplikasi ini mirip dengan menggunakan operator GOTO untuk beralih dari fungsi ke fungsi.
3. Kisah RomawiLisp mendukung sistem angka, yang sudah ketinggalan zaman sebelum kemunculannya.
Gunakan
Berikut adalah beberapa contoh kode sederhana.
(function crc8 (data :optional seed) (var result (if-nil seed 0)) (var s_data data) (for bit 8 (if (= (bit-and (bit-xor result s_data) $01) 0) (set result (shift result -1 8)) (else (set result (bit-xor result $18)) (set result (shift result -1 8)) (set result (bit-or result $80)))) (set s_data (shift s_data -1 8))) result)
Implementasi
Penerjemah ditulis dalam Delphi (FreePascal dalam mode kompatibilitas). Itu dibangun di Lazarus 1.6.2 dan lebih tinggi, di bawah Windows dan Linux 32 dan 64 bit. Dari dependensi eksternal, memerlukan libmysql.dll. Berisi sekitar 15_2000..20_000 baris. Ada sekitar 200 fungsi bawaan untuk berbagai keperluan (beberapa kelebihan beban delapan kali).
Disimpan di siniDukungan untuk pengetikan dinamis dilakukan dengan cara sepele - semua tipe data yang diproses diwakili oleh ahli waris dari kelas TValue yang sama.
Jenis yang paling penting untuk Lisp - daftarnya adalah, seperti kebiasaan di Delphi, kelas yang berisi array dinamis objek tipe TValue. Untuk jenis ini, mekanisme CopyOnWrite diimplementasikan.
Manajemen memori otomatis berdasarkan penghitungan referensi. Untuk struktur rekursif, semua tautan dalam struktur dihitung secara bersamaan. Pelepasan memori dimulai segera ketika variabel keluar dari lingkup. Tidak ada mekanisme untuk menunda awal pengumpul sampah.
Penanganan pengecualian bekerja pada mekanisme yang dibangun ke dalam Delphi. Dengan demikian, kesalahan yang terjadi pada kode interpreter dapat diproses oleh kode yang dapat dieksekusi pada Fox.
Setiap operator atau fungsi Lisi bawaan diimplementasikan sebagai metode atau fungsi dalam kode juru bahasa. Script dieksekusi oleh panggilan implementasi yang saling rekursif. Kode juru bahasa dan skrip memiliki tumpukan panggilan umum.
Variabel skrip disimpan dalam memori dinamis secara independen. Setiap fungsi yang ditentukan pengguna memiliki tumpukan sendiri untuk menyimpan referensi variabel, terlepas dari tumpukan tingkat atas atau tumpukan fungsi induk.
Kesulitan khusus adalah implementasi operator penugasan (set) untuk elemen struktural. Menghitung langsung pointer ke elemen yang diperlukan mengarah pada risiko menggantung tautan, karena sintaksis Lisi tidak melarang memodifikasi struktur selama perhitungan elemen yang diperlukan. Sebagai solusi kompromi, "penunjuk rantai" diimplementasikan - objek yang berisi referensi ke variabel dan array indeks numerik untuk menunjukkan jalur di dalam struktur. Pointer seperti itu juga rentan terhadap masalah tautan yang menggantung, tetapi jika terjadi kegagalan, ia menghasilkan pesan kesalahan yang bermakna.
Alat pengembangan
1. Konsol2. Editor teksDilengkapi dengan penyorotan sintaks dan kemampuan untuk menjalankan skrip yang dapat diedit di F9.

Kesimpulan
Dalam kondisi saat ini, proyek menyelesaikan masalah yang dikandungnya, dan tidak memerlukan pengembangan aktif lebih lanjut. Banyak ketidaksempurnaan yang ada tidak mempengaruhi pekerjaan secara signifikan.