Ngôn ngữ Python homoiconic
(aljamal.substack.com)Triển khai "Lisp in Lisp" của McCarthy bằng Python
-
Lisp do John McCarthy phát triển vào đầu những năm 1960 có đặc tính homoiconicity, nên mã và dữ liệu có thể hoán đổi cho nhau
- Trong Lisp, ranh giới giữa mã và dữ liệu trở nên mơ hồ
- Nhờ đó, Lisp có thể tự biểu diễn chính nó một cách tự nhiên
-
Nửa trang mã ở cuối trang 13 của tài liệu hướng dẫn Lisp 1.5 chính là bản thân Lisp
- Alan Kay gọi nó là "phương trình Maxwell của phần mềm"
- Toàn bộ thế giới lập trình được nén lại chỉ trong vài dòng mã
-
Để hiểu điều này, một cách hay là dùng Python để tái hiện lại mã "Lisp in Lisp"
- Phần lớn lập trình viên không quen với cú pháp Lisp nhưng sẽ quen với cú pháp Python hơn
- Mục tiêu là viết lại bằng Python trong khi vẫn giữ tối đa tinh thần của mã Lisp
M-expression và S-expression của Lisp
-
Ban đầu Lisp có hai dạng cú pháp
- M-expression (meta expression): dạng cú pháp cho mã
- S-expression (symbolic expression): dạng cú pháp cho dữ liệu
- Về mặt ngữ nghĩa, hai dạng này là tương đương
-
Mã "Lisp in Lisp" được viết bằng M-expression và triển khai Lisp S-expression
-
Một cách tiếp cận là chuyển M-expression của Lisp sang cấu trúc mã Python, còn S-expression thì biểu diễn bằng danh sách Python
- Lisp là viết tắt của List Processing, chỉ dùng một cấu trúc dữ liệu duy nhất là danh sách
Lần triển khai thứ nhất
-
Triển khai các phép toán cơ bản của Lisp bằng những hàm cơ bản của danh sách Python
- atom(x): kiểm tra x có phải là danh sách hay không
- eq(x,y): kiểm tra x và y có giống nhau hay không
- car(x): trả về phần tử đầu tiên của danh sách
- cdr(x): trả về phần còn lại của danh sách
- cons(x,y): thêm một atom vào danh sách
- append(x,y): nối hai danh sách
-
Bỏ qua một vài thành phần đệ quy cơ bản, có thể nhanh chóng triển khai một trình thông dịch cho tập con của mã "Lisp in Lisp" với sự trợ giúp của Llama3-70b
Lần triển khai thứ hai
-
Ở lần triển khai đầu tiên, chức năng lambda còn thiếu
- Lambda là cách chủ yếu để định nghĩa và gọi hàm trong Lisp
- Không có lambda thì không thể triển khai đệ quy, và không có đệ quy thì sẽ không Turing-complete
-
Để triển khai lambda cần có các thành phần cơ bản assoc(x,y) và pairlis(x,y)
- assoc(x,y) là tra cứu từ điển key/value bằng association list
- pairlis(x,y) tương đương với
zip(x,y)của Python
-
Lisp không có loop nên ngay cả tìm kiếm tuyến tính đơn giản cũng phải dùng đệ quy
- Nhưng trong Python có thể chuyển đổi thanh lịch bằng list comprehension
- evcon và evlis cũng tương tự, có thể chuyển thành loop
-
Hàm eval trong Lisp nguyên bản nhận hai đối số
- Đối số thứ nhất là biểu thức (s-exp), đối số thứ hai là môi trường (environment) dưới dạng danh sách key/value
- Môi trường giữ các ràng buộc biến của LAMBDA
- Sử dụng kỹ thuật dynamic scoping
Ý kiến của GN⁺
-
Đây là một nỗ lực thú vị nhằm mô phỏng đặc tính homoiconicity của Lisp bằng Python. Tuy nhiên, có vẻ vẫn có giới hạn trong việc chuyển tải hoàn hảo những đặc tính riêng của Lisp. Nếu muốn cảm nhận sức hấp dẫn của Lisp, có lẽ tốt nhất vẫn là học chính Lisp.
-
Việc triển khai các tính năng mạnh mẽ của Lisp như hàm lambda và dynamic scoping bằng Python là rất ấn tượng. Tuy vậy, dường như vẫn còn nhiều điểm thiếu sót để áp dụng vào dự án thực tế. Với mục đích giáo dục hoặc nghiên cứu thì có vẻ vẫn rất đáng giá.
-
Bản thân những thử nghiệm như vậy có thể trở thành dịp để suy nghĩ sâu hơn về bản chất của ngôn ngữ lập trình. Vượt qua cú pháp và cách hiện thực của từng ngôn ngữ, ta có thể có được góc nhìn từ phương diện paradigm lập trình.
-
Việc học các ngôn ngữ họ Lisp có thể giúp mang lại những hiểu biết sâu sắc về lập trình hàm và meta-programming. Cũng đáng để xem qua các ngôn ngữ họ Lisp hiện đại như Scheme, Clojure, Racket.
1 bình luận
Một phương ngữ Lisp được nhúng trong Python
https://hylang.org/