ภาษา Python แบบโฮโมไอโคนิค (Homoiconic)
(aljamal.substack.com)การนำ "Lisp in Lisp" ของ McCarthy มาเขียนด้วย Python
-
Lisp ที่ John McCarthy พัฒนาขึ้นในช่วงต้นทศวรรษ 1960 มีคุณสมบัติแบบ homoiconicity ทำให้โค้ดและข้อมูลสามารถสลับแทนกันได้
- ใน Lisp เส้นแบ่งระหว่างโค้ดกับข้อมูลจึงไม่ชัดเจน
- ด้วยเหตุนี้ Lisp จึงสามารถอธิบายตัวเองได้อย่างเป็นธรรมชาติ
-
โค้ดเพียงครึ่งหน้าที่ด้านล่างของหน้าที่ 13 ในคู่มือ Lisp 1.5 ก็คือตัว Lisp เอง
- Alan Kay เรียกสิ่งนี้ว่า "สมการแมกซ์เวลล์ของซอฟต์แวร์"
- โลกทั้งใบของการเขียนโปรแกรมถูกบีบอัดอยู่ในโค้ดเพียงไม่กี่บรรทัด
-
วิธีที่ดีในการทำความเข้าใจเรื่องนี้คือการนำโค้ด "Lisp in Lisp" มาเขียนใหม่ด้วย Python
- โปรแกรมเมอร์ส่วนใหญ่ไม่คุ้นกับไวยากรณ์ของ Lisp แต่คุ้นกับไวยากรณ์ของ Python มากกว่า
- เป้าหมายคือเขียนใหม่ด้วย Python โดยคงจิตวิญญาณของโค้ด Lisp ไว้ให้มากที่สุด
M-expression และ S-expression ของ Lisp
-
เดิมที Lisp มีไวยากรณ์อยู่สองแบบ
- M-expression (meta expression): รูปแบบสำหรับโค้ด
- S-expression (symbolic expression): รูปแบบสำหรับข้อมูล
- ทั้งสองแบบมีความหมายเหมือนกัน
-
โค้ด "Lisp in Lisp" ถูกเขียนด้วย M-expression และใช้เพื่ออิมพลีเมนต์ S-expression Lisp
-
วิธีหนึ่งคือแปลง M-expression ของ Lisp ให้เป็นโครงสร้างโค้ดแบบ Python และแทน S-expression ด้วยลิสต์ของ Python
- Lisp ย่อมาจาก List Processing และใช้โครงสร้างข้อมูลเพียงแบบเดียวคือลิสต์
การอิมพลีเมนต์ครั้งแรก
-
ใช้ฟังก์ชันพื้นฐานของลิสต์ใน Python เพื่ออิมพลีเมนต์การดำเนินการพื้นฐานของ Lisp
- atom(x): ตรวจว่า x เป็นลิสต์หรือไม่
- eq(x,y): ตรวจว่า x และ y เท่ากันหรือไม่
- car(x): คืนค่าองค์ประกอบแรกของลิสต์
- cdr(x): คืนค่าส่วนที่เหลือของลิสต์
- cons(x,y): เพิ่มอะตอมเข้าไปในลิสต์
- append(x,y): รวมลิสต์สองอันเข้าด้วยกัน
-
แม้จะละเลยองค์ประกอบพื้นฐานบางส่วนของการเรียกซ้ำไป แต่ก็สามารถใช้ความช่วยเหลือจาก Llama3-70b เพื่ออิมพลีเมนต์อินเทอร์พรีเตอร์สำหรับบางส่วนของโค้ด "Lisp in Lisp" ได้อย่างรวดเร็ว
การอิมพลีเมนต์ครั้งที่สอง
-
ในการอิมพลีเมนต์ครั้งแรกยังไม่มีความสามารถเรื่อง lambda
- lambda เป็นวิธีหลักในการนิยามและเรียกใช้ฟังก์ชันใน Lisp
- หากไม่มี lambda ก็ไม่สามารถอิมพลีเมนต์ recursion ได้ และหากไม่มี recursion ก็จะไม่ Turing-complete
-
การอิมพลีเมนต์ lambda ต้องมีองค์ประกอบพื้นฐาน assoc(x,y) และ pairlis(x,y)
- assoc(x,y) คือการ lookup พจนานุกรมแบบ key/value โดยใช้ association list
- pairlis(x,y) มีความหมายเหมือนกับ
zip(x,y)ของ Python
-
Lisp ไม่มี loop จึงต้องใช้ recursion แม้แต่กับการค้นหาเชิงเส้นอย่างง่าย
- แต่ใน Python สามารถแปลงได้อย่างสวยงามด้วย list comprehension
- เช่นเดียวกันกับ evcon และ evlis ที่แปลงเป็น loop ได้
-
เดิมทีฟังก์ชัน eval ใน Lisp รับอาร์กิวเมนต์สองตัว
- ตัวแรกคือ expression (s-exp) และตัวที่สองคือ environment ซึ่งเป็นลิสต์ key/value
- environment ใช้เก็บการผูกตัวแปรของ LAMBDA
- ใช้เทคนิค dynamic scoping
ความเห็นของ GN⁺
-
นี่เป็นความพยายามที่น่าสนใจในการเลียนแบบคุณสมบัติ homoiconicity ของ Lisp ด้วย Python แต่ก็ดูเหมือนจะมีข้อจำกัดในการถ่ายทอดลักษณะเฉพาะของ Lisp ได้อย่างสมบูรณ์ หากอยากสัมผัสเสน่ห์ของ Lisp จริง ๆ การเรียนรู้ Lisp โดยตรงน่าจะดีที่สุด
-
การอิมพลีเมนต์ฟีเจอร์ทรงพลังของ Lisp เช่น lambda function และ dynamic scoping ด้วย Python นั้นน่าประทับใจ แต่ก็ดูเหมือนยังมีข้อจำกัดมากหากจะนำไปใช้กับโปรเจกต์จริง อย่างไรก็ตาม สำหรับการศึกษาและการวิจัยก็น่าจะมีคุณค่า
-
ความพยายามแบบนี้อาจเป็นจุดเริ่มต้นให้ได้คิดลึกขึ้นถึงธรรมชาติของภาษาโปรแกรมมิงเอง ไม่ใช่มองแค่ไวยากรณ์หรือการอิมพลีเมนต์ของภาษา แต่รวมถึงมุมมองในเชิง programming paradigm ด้วย
-
การเรียนรู้ภาษาตระกูล Lisp อาจช่วยให้เข้าใจ functional programming และ meta-programming ได้ลึกขึ้น และก็น่าสนใจที่จะลองดูภาษาตระกูล Lisp สมัยใหม่อย่าง Scheme, Clojure และ Racket ด้วย
1 ความคิดเห็น
ภาษาถิ่นของ Lisp ที่ฝังอยู่ใน Python
https://hylang.org/