Bahasa Python yang Homoiconic
(substack.com/aljamal)- Homoiconicity dalam Lisp adalah sifat yang memperlakukan kode dan data dalam bentuk yang sama, dan ketika “Lisp in Lisp” klasik dipindahkan ke Python, ide ini tampak dalam sintaks yang lebih familier
- Lisp pada awalnya memiliki M-expression sebagai representasi kode dan S-expression sebagai representasi data, dan “Lisp in Lisp” mengimplementasikan Lisp berbasis S-expression dengan M-expression
- Versi Python merepresentasikan S-expression sebagai list Python, dan memindahkan M-expression ke pemanggilan fungsi serta pernyataan kondisional sehingga interpreter dapat dibangun tanpa parser terpisah
- Interpreter pertama berdiri di atas operasi primitif list seperti
atom,eq,car,cdr,cons,append, dan untuk mendukunglambdaditambahkanassoc,pairlis, serta list lingkungan evalmenerima ekspresi dan lingkungan secara bersamaan untuk menangani pengikatan variabel, dan menghubungkan argumen serta nilai melaluipairlisdanassocdengan pendekatan dynamic scope
Integrasi kode dan data yang diperlihatkan Lisp
- Lisp meninggalkan banyak gagasan yang tetap relevan beberapa dekade kemudian melalui Lisp paper dan Lisp 1.5 manual karya John McCarthy pada awal 1960-an
- Inti di antaranya adalah homoiconicity
- Dalam bahasa pada umumnya, kode dipahami sebagai rangkaian operasi yang bekerja pada data
- Lisp memperlakukan kode dan data dalam bentuk yang sama sehingga batas antara operator dan operand menjadi kabur
- Alan Kay menyebut kode “Lisp in Lisp” di bagian bawah halaman 13 dari Lisp 1.5 manual sebagai “Maxwell's Equations of Software”
- Terdapat kutipan dengan maksud bahwa beberapa baris kode itu memberinya pencerahan besar karena memuat seluruh dunia pemrograman
Cara memindahkan “Lisp in Lisp” ke Python
- Tujuannya adalah menulis ulang kode “Lisp in Lisp” klasik dalam Python sambil sebisa mungkin mempertahankan semangat kode aslinya
- Lisp memiliki dua bentuk representasi sintaksis
- M-expression: representasi kode, singkatan dari meta
- S-expression: representasi data, singkatan dari symbolic
- Keduanya setara secara semantik
- Kode “Lisp in Lisp” yang ada ditulis dalam M-expression untuk mengimplementasikan Lisp berbasis S-expression
- Dalam implementasi Python, Lisp S-expression direpresentasikan sebagai list Python
- Lisp adalah singkatan dari “List Processing” dan bekerja dengan berpusat pada satu struktur data, yaitu list
- List Python digunakan sebagai representasi yang cocok untuk mengemulasikan Lisp S-expression
- M-expression diterjemahkan ke struktur kode Python seperti pemanggilan fungsi dan pernyataan kondisional
- Berkat pemetaan ini, interpreter dapat dibuat tanpa manipulasi string atau implementasi parser terpisah
Interpreter pertama yang dibangun dari operasi primitif list
- Implementasi Lisp memerlukan beberapa fungsi dasar yang disediakan dari luar bahasa
- Operasi primitif list yang digunakan dalam implementasi Python adalah sebagai berikut
atom(x): memeriksa apakahxadalah listeq(x,y): memeriksa apakahxdanysamacar(x): elemen pertama dari listcdr(x): sisa dari listcons(x,y): menempelkan atom ke listappend(x,y): menyambungkan dua list
- Selain beberapa operasi primitif rekursif, Llama3-70b dimanfaatkan melalui Groq untuk dengan cepat membuat interpreter yang menjalankan subset dari kode “Lisp in Lisp”
- Dalam contoh, list Python berfungsi seperti S-expression
- Kode lengkap tersedia di github gists
Ekstensi untuk lambda dan rekursi
- Implementasi pertama belum memiliki fitur penting, yaitu
lambdalambdaadalah cara utama di Lisp untuk mendefinisikan dan memanggil fungsi anonim- Tanpa
lambda, Lisp tidak dapat mengimplementasikan rekursi - Tanpa rekursi, ia tidak mencapai kelengkapan Turing, yaitu syarat minimum untuk menghitung segala sesuatu yang dapat dihitung
- Untuk menambahkan
lambda, ditambahkanassoc(x,y)danpairlis(x,y)assoc(x,y)adalah pencarian kunci/nilai yang diimplementasikan dengan list, menggunakan association listpairlis(x,y)memasangkan dua list sepertizip(x,y)di Python
- Lisp asli harus menangani bahkan pemindaian linear sederhana dengan rekursi
- Karena Lisp asli tidak memiliki loop
- Dalam terjemahan Python,
assocdanpairlisdapat diekspresikan lebih ringkas dengan list comprehension
- Dalam penanganan
COND,evconpada Lisp asli diterjemahkan menjadi loop, dan pendekatan yang sama diterapkan padaevlisdalam penangananLAMBDA
List lingkungan dan dynamic scope
- Fungsi
evalpada Lisp asli menerima dua argumen- Argumen pertama adalah S-expression yang akan dievaluasi
- Argumen kedua adalah list lingkungan yang berisi pasangan kunci/nilai
- Lingkungan mempertahankan pengikatan variabel dalam penanganan
LAMBDA- Jika sebuah fungsi memiliki variabel
xdan data diberikan ke dalamnya,pairlismemasangkan simbolxdengan data tersebut - Nilai yang dipasangkan disimpan atau ditambahkan ke list lingkungan
- Saat
xdiperlukan,assocmencarinya di lingkungan dan memasukkannya kembali ke ekspresi
- Jika sebuah fungsi memiliki variabel
- Cara pengikatan ini disebut dynamic scope
- Implementasi akhirnya merupakan bentuk Python dari “Lisp in Lisp” asli, dan pada contoh terakhir juga mencakup eksekusi
lambda
2 komentar
Opini Hacker News
Jika tertarik pada Lisp untuk scripting bergaya Python yang sederhana dan cepat, bahasa bergaya Clojure seperti Hy dan Janet layak dilihat
Disediakan sebagai binary yang cepat dijalankan, dan juga menyertakan library untuk tugas sehari-hari
Untuk sisi CL↔Python, https://github.com/CodyReichert/awesome-cl?tab=readme-ov-fil... juga layak dijadikan rujukan
Saya adalah maintainer Hy, dan kalau terlihat ada perbedaan performa yang berarti, itu bug
M-expression menghindari jebakan ini, jadi saya penasaran apakah pernah ada bahasa nyata yang mempertahankan homoiconicity dan keanggunan konseptual Lisp, tetapi tidak mensyaratkan tanda kurung seperti itu
Tepat seperti yang terpikirkan. Penjelasannya juga sangat terus terang: “Program in YAML”
Hampir tidak perlu dijelaskan lagi
Tunggu, bukankah ini cuma Lisp yang diimplementasikan dengan Python?
Sepertinya bukan Python yang homoiconic seperti yang disiratkan judulnya; apa yang saya lewatkan?
Untuk itu, sintaks Python harus tersusun dari tuple, list, dan dictionary, lalu interpreter harus mengevaluasinya secara langsung
Bahasa fungsional lain yang bisa diimplementasikan secara ringkas dengan Python adalah Binary Lambda Calculus
Sebagian besar kodenya menangani model input/output murni BLC, dan untuk lookup variabel menggunakan indeks de Bruijn untuk mengindeks array environment alih-alih association list
Di halaman yang sama juga ada implementasi dalam 9 bahasa lain, dan self-interpreter BLC adalah yang paling ringkas dengan 232 bit (29 byte), termasuk parser dan tokenizer
[1] https://rosettacode.org/wiki/Universal_Lambda_Machine#Python
Mata kuliah dasar pemrograman MIT meminta semua mahasiswa menulis interpreter Lisp dengan Python
Itu adalah jejak dari masa ketika kuliah tersebut dulu benar-benar diajarkan menggunakan Lisp
https://py.mit.edu/spring24
Saya pernah membuat sesuatu yang mirip dengan list JS: https://github.com/andrelaszlo/js-lisp
Lucu sekaligus ironis bahwa bahasa-bahasa modern, puluhan tahun kemudian, menemukan kembali fitur-fitur menakjubkan dari Lisp
Beberapa hari lalu saya kesal karena sebuah program Python yang memanggil API server rumah saya selama 9 jam berhenti. Panggilannya mirip-mirip, dan program itu memanggil LLM dengan template prompt yang sudah ditentukan sebelumnya serta batasan tata bahasa
Sebelum menjalankan sisa iterasi secara terpisah, saya ingin menyimpan status program lalu keluar, tetapi saya tidak menemukan cara untuk mengubah kode Python yang sedang berjalan atau mengintip variabelnya, sehingga akhirnya pekerjaan 9 jam hilang
Beberapa hari kemudian saya melihat https://malisper.me/debugging-lisp-part-1-recompilation/, dan saya terkejut mengetahui bahwa Common Lisp sudah menyematkan kemampuan seperti ini di dalam bahasa sejak puluhan tahun lalu. Begitu juga fitur-fitur lain, seperti sistem makro yang paling kuat
Namun itu juga masalahnya. Ekspresivitasnya terlalu kuat
Lisp selalu mengingatkan saya pada seni visual media campuran; kebebasan berekspresi tampak bagus, tetapi pada akhirnya hasilnya sering kali secara umum kalah dibanding seni satu media yang lebih tradisional. Terlihat bahwa batasan medium sama pentingnya dengan ekspresivitas
Atau ada juga https://github.com/malor/cpython-lldb
Lebih banyak cara ada di https://github.com/albertz/pydbattach/
Bagaimana implementasi ini bisa disebut Python homoikonik?
Apakah ada Lisp yang memiliki sistem tipe yang sampai taraf tertentu membantu mengendalikan kecenderungan Lisp menjadi sulit dibaca seiring program membesar?
Meski bukan sistem tipe, saya penasaran apakah ada unsur lain yang bisa mengendalikan sifat semacam itu
Metaprogramming memang keren, tetapi kadang terasa seperti abstraksi Haskell paling pelik yang pernah saya lihat, namun bahkan tanpa type signature yang bisa menjadi penunjuk arah
Saya menganggap sistem tipe dan linter sebagai alat terbaik untuk menjinakkan kode secara otomatis, tetapi sulit membayangkan bagaimana menekan kecenderungan proyek Lisp tanpa membatasi alasan utama memilih Lisp itu sendiri
SBCL, yang praktis paling dekat dengan standar de facto, juga punya pemeriksaan tipe saat kompilasi yang cukup baik
Misalnya, jika setelah
(declare (type String a b))Anda menulis(+ a b), ia akan memberi peringatan bahwa tipe turunanAadalahSTRINGpadahal seharusnyaNUMBERAlasan terbesar saya pindah dari Scheme ke CL adalah pemeriksaan tipe ini, dan setelah itu saya tetap bertahan karena fitur-fitur kecil yang menyenangkan seperti restarts dan continuable asserts
Jika ada Python yang berorientasi ekspresi, itu akan jauh lebih baik daripada Python sekarang
Tulisan ini bukan seperti yang diisyaratkan judulnya, tetapi bagus sebagai penjelasan tentang Lisp
Itu tetap tidak apa-apa, tetapi “menempatkan ekspresi sebagai tata bahasa kelas dua” adalah pilihan yang cukup mendasar dan disengaja. Itu juga desain untuk menyulitkan developer yang ingin menulis sup tanda kurung
Karena itu
lambda:dan:=sengaja dibuat kaku untuk menghambat pemakaian, dan fungsi anonim pun tidak bisa ditulisPython adalah bahasa yang mengatakan bahwa jika ingin melakukan hal cerdas, lakukanlah dengan iterator, bukan objek callable
Saya berharap semuanya punya tanpa-sintaks ala Lisp
Bagi saya itu lebih baik, dan untungnya di CL hal itu mudah dilakukan
Dialek Lisp yang ditanamkan di dalam Python
https://hylang.org/