2 poin oleh GN⁺ 2024-05-13 | 2 komentar | Bagikan ke WhatsApp
  • 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 mendukung lambda ditambahkan assoc, pairlis, serta list lingkungan
  • eval menerima ekspresi dan lingkungan secara bersamaan untuk menangani pengikatan variabel, dan menghubungkan argumen serta nilai melalui pairlis dan assoc dengan 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 apakah x adalah list
    • eq(x,y): memeriksa apakah x dan y sama
    • car(x): elemen pertama dari list
    • cdr(x): sisa dari list
    • cons(x,y): menempelkan atom ke list
    • append(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 lambda
    • lambda adalah 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, ditambahkan assoc(x,y) dan pairlis(x,y)
    • assoc(x,y) adalah pencarian kunci/nilai yang diimplementasikan dengan list, menggunakan association list
    • pairlis(x,y) memasangkan dua list seperti zip(x,y) di Python
  • Lisp asli harus menangani bahkan pemindaian linear sederhana dengan rekursi
    • Karena Lisp asli tidak memiliki loop
    • Dalam terjemahan Python, assoc dan pairlis dapat diekspresikan lebih ringkas dengan list comprehension
  • Dalam penanganan COND, evcon pada Lisp asli diterjemahkan menjadi loop, dan pendekatan yang sama diterapkan pada evlis dalam penanganan LAMBDA

List lingkungan dan dynamic scope

  • Fungsi eval pada 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 x dan data diberikan ke dalamnya, pairlis memasangkan simbol x dengan data tersebut
    • Nilai yang dipasangkan disimpan atau ditambahkan ke list lingkungan
    • Saat x diperlukan, assoc mencarinya di lingkungan dan memasukkannya kembali ke ekspresi
  • 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

 
GN⁺ 2024-05-13
Opini Hacker News
  • Jika tertarik pada Lisp untuk scripting bergaya Python yang sederhana dan cepat, bahasa bergaya Clojure seperti Hy dan Janet layak dilihat

    1. Hy(https://hylang.org/): dikompilasi menjadi bytecode Python, biasanya lebih lambat daripada Python, tetapi kompatibel dengan semua library Python
    2. Janet(https://janet-lang.org/): virtual machine tertanam yang sangat ringan bergaya Lua, sekitar 1MB, kira-kira 2 kali lebih cepat daripada Python pada operasi serupa, dan interoperabilitas dengan C sangat mudah
    • Untuk scripting bergaya Python yang cepat, ada juga https://github.com/ciel-lang/ciel, Common Lisp dengan baterai lengkap
      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
    • Hy lebih lambat dari Python? Setidaknya pada waktu eksekusi seharusnya tidak begitu
      Saya adalah maintainer Hy, dan kalau terlihat ada perbedaan performa yang berarti, itu bug
      1. Basilisp(https://github.com/basilisp-lang/basilisp): “dialek Lisp yang kompatibel-ish dengan Clojure dan menargetkan Python 3.8+”
    • Mungkin terdengar seperti selera pribadi, tetapi Lisp selalu terasa seperti bahasa yang mengundang bantahan karena tanda kurung pembungkusnya yang wajib
      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
    • https://github.com/yaml/yamlscript
      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?

    • Menurut hukum kesepuluh Greenspun, itu benar: https://en.m.wikipedia.org/wiki/Greenspun's_tenth_rule
    • Saya juga sempat cukup antusias karena mengira ini Python homoiconic yang bisa saya pakai tanpa harus membuat rekan-rekan menerima Lisp, tetapi ternyata bukan, cuma Lisp
    • Kelihatannya begitu. Saya penasaran karena ingin melihat Python homoiconic ini, tetapi saya juga tidak yakin apakah itu mungkin
      Untuk itu, sintaks Python harus tersusun dari tuple, list, dan dictionary, lalu interpreter harus mengevaluasinya secara langsung
    • Apa bedanya?
  • 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

    • Serupa, mata kuliah yang saya ambil di universitas juga meminta kami mengimplementasikan interpreter Lisp dasar dengan Lisp
  • 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

    • Tidak mengherankan kalau bahasa modern menemukan kembali fitur Lisp puluhan tahun kemudian. Rumpun Lisp adalah bahasa-bahasa yang paling fleksibel dan ekspresif
      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
    • Untuk mengintip status sampai batas tertentu, Anda bisa memakai Py-spy atau Pystack
      Atau ada juga https://github.com/malor/cpython-lldb
      Lebih banyak cara ada di https://github.com/albertz/pydbattach/
    • Untuk proses yang berjalan berjam-jam, sebaiknya pekerjaan disimpan ke disk dan siap dilanjutkan lagi dari titik terhenti
    • Tidak ada exception yang dilempar?
  • 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

    • Kalau Lisp dengan sistem tipe, yang terlintas adalah Common Lisp
      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 turunan A adalah STRING padahal seharusnya NUMBER
      Alasan 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
    • Di sisi yang ramah dan mudah didekati ada Typed Racket, sedangkan di sisi yang kuat tetapi kompleks ada Shen
    • https://en.wikipedia.org/wiki/Dylan_(programming_language)
  • 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

    • Python yang berorientasi ekspresi mungkin bukan Python lagi
      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 ditulis
      Python 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

 
kayws426 2024-05-13

Dialek Lisp yang ditanamkan di dalam Python
https://hylang.org/