ตัวแปร Lisp เต็มแบบคงที่เป็นไปได้หรือไม่? มันสมเหตุสมผลหรือไม่ที่สิ่งนี้จะมีอยู่จริง? ฉันเชื่อว่าหนึ่งในคุณธรรมของภาษา Lisp คือความเรียบง่ายของคำจำกัดความ การพิมพ์แบบคงที่จะประนีประนอมหลักการสำคัญนี้หรือไม่?
ตัวแปร Lisp เต็มแบบคงที่เป็นไปได้หรือไม่? มันสมเหตุสมผลหรือไม่ที่สิ่งนี้จะมีอยู่จริง? ฉันเชื่อว่าหนึ่งในคุณธรรมของภาษา Lisp คือความเรียบง่ายของคำจำกัดความ การพิมพ์แบบคงที่จะประนีประนอมหลักการสำคัญนี้หรือไม่?
คำตอบ:
ใช่เป็นไปได้มากแม้ว่าระบบประเภท HM-style มาตรฐานมักจะเป็นตัวเลือกที่ไม่ถูกต้องสำหรับรหัส Lisp / Scheme สำนวนส่วนใหญ่ ดูTyped Racketสำหรับภาษาล่าสุดที่เป็น "Full Lisp" (เหมือน Scheme มากกว่า) ที่มีการพิมพ์แบบคงที่
Sexpr
ที่มักจะเป็น
coerce :: a->b
ในรูปแบบของ eval ประเภทความปลอดภัยอยู่ที่ไหน?
eval
คุณต้องทดสอบผลลัพธ์เพื่อดูว่ามีอะไรออกมาบ้างซึ่งไม่มีอะไรใหม่ใน Typed Racked (จัดการเช่นเดียวกับฟังก์ชันที่ใช้ประเภทการรวมกันString
และNumber
) วิธีที่ชัดเจนเพื่อดูว่าสิ่งนี้สามารถทำได้คือคุณสามารถเขียนและใช้ภาษาที่พิมพ์แบบไดนามิกในภาษาที่พิมพ์ด้วย HM-statically
หากสิ่งที่คุณต้องการคือภาษาที่พิมพ์แบบคงที่ซึ่งดูเหมือน Lisp คุณสามารถทำได้ค่อนข้างง่ายโดยการกำหนดโครงสร้างไวยากรณ์นามธรรมที่แสดงถึงภาษาของคุณแล้วแมป AST นั้นกับนิพจน์ S อย่างไรก็ตามฉันไม่คิดว่าฉันจะเรียกผลลัพธ์ว่า Lisp
หากคุณต้องการบางสิ่งที่มีลักษณะ Lisp-y จริง ๆ นอกเหนือจากไวยากรณ์คุณสามารถทำได้ด้วยภาษาที่พิมพ์แบบคงที่ อย่างไรก็ตามมีหลายลักษณะของเสียงกระเพื่อมที่ยากต่อการพิมพ์แบบคงที่ที่มีประโยชน์มาก เพื่อเป็นตัวอย่างเรามาดูโครงสร้างรายการที่เรียกว่าข้อเสียซึ่งเป็นส่วนประกอบหลักของ Lisp
การเรียกข้อเสียว่ารายการแม้ว่าจะ(1 2 3)
ดูเหมือนเป็นการเรียกชื่อผิดเล็กน้อย ตัวอย่างเช่นไม่สามารถเทียบเคียงได้กับรายการที่พิมพ์แบบคงที่เช่น C ++ std::list
หรือรายการของ Haskell รายการเหล่านี้เป็นรายการที่เชื่อมโยงแบบมิติเดียวโดยที่เซลล์ทั้งหมดเป็นประเภทเดียวกัน (1 "abc" #\d 'foo)
เสียงกระเพื่อมอย่างมีความสุขช่วย นอกจากนี้แม้ว่าคุณจะขยายรายการที่พิมพ์แบบคงที่ของคุณเพื่อให้ครอบคลุมรายการของรายการประเภทของวัตถุเหล่านี้ต้องการให้ทุกองค์ประกอบของรายการเป็นรายการย่อย คุณจะเป็นตัวแทนของ((1 2) 3 4)
พวกเขาอย่างไร?
Lisp Conses ก่อตัวเป็นต้นไม้ไบนารีโดยมีใบไม้ (อะตอม) และกิ่งก้าน (conses) นอกจากนี้ใบของต้นไม้ดังกล่าวอาจมีประเภท Lisp ของอะตอม (ไม่ใช่ข้อเสีย) เลยก็ได้! ความยืดหยุ่นของโครงสร้างนี้คือสิ่งที่ทำให้ Lisp สามารถจัดการการคำนวณสัญลักษณ์ AST และการเปลี่ยนรหัส Lisp ได้เอง!
คุณจะสร้างโมเดลโครงสร้างดังกล่าวในภาษาที่พิมพ์แบบคงที่ได้อย่างไร? ลองใช้ใน Haskell ซึ่งมีระบบประเภทสถิตที่ทรงพลังและแม่นยำมาก:
type Symbol = String
data Atom = ASymbol Symbol | AInt Int | AString String | Nil
data Cons = CCons Cons Cons
| CAtom Atom
ปัญหาแรกของคุณคือขอบเขตของประเภท Atom เห็นได้ชัดว่าเรายังไม่ได้เลือกประเภท Atom ที่มีความยืดหยุ่นเพียงพอที่จะครอบคลุมวัตถุทุกประเภทที่เราต้องการจะโหนสลิง แทนที่จะพยายามขยายโครงสร้างข้อมูล Atom ตามที่ระบุไว้ด้านบน (ซึ่งคุณเห็นได้ชัดว่าเปราะ) สมมติว่าเรามีคลาสประเภทเวทย์มนตร์Atomic
ที่แยกแยะประเภททั้งหมดที่เราต้องการสร้างอะตอม จากนั้นเราอาจลอง:
class Atomic a where ?????
data Atomic a => Cons a = CCons Cons Cons
| CAtom a
แต่จะใช้ไม่ได้เพราะต้องใช้อะตอมทั้งหมดในต้นไม้เป็นประเภทเดียวกัน เราต้องการให้พวกมันสามารถแตกต่างกันไปในแต่ละใบไม้ แนวทางที่ดีกว่าต้องใช้ตัวระบุปริมาณที่มีอยู่ของ Haskell :
class Atomic a where ?????
data Cons = CCons Cons Cons
| forall a. Atomic a => CAtom a
แต่ตอนนี้คุณมาถึงปมของเรื่องแล้ว อะตอมในโครงสร้างแบบนี้ทำอะไรได้บ้าง? โครงสร้างมีอะไรเหมือนกันที่สามารถจำลองได้ด้วยAtomic a
? ประเภทดังกล่าวรับประกันความปลอดภัยในระดับใด โปรดทราบว่าเราไม่ได้เพิ่มฟังก์ชันใด ๆ ลงในคลาส type ของเราและมีเหตุผลที่ดี: อะตอมไม่มีอะไรเหมือนกันใน Lisp supertype ใน Lisp เรียกง่ายๆว่าt
(คือด้านบน)
ในการใช้งานคุณจะต้องสร้างกลไกในการบังคับค่าของอะตอมแบบไดนามิกกับสิ่งที่คุณสามารถใช้ได้จริง และเมื่อถึงจุดนั้นคุณได้ติดตั้งระบบย่อยที่พิมพ์แบบไดนามิกภายในภาษาที่พิมพ์แบบคงที่! (เราไม่สามารถช่วยได้ แต่สังเกตข้อพิสูจน์ที่เป็นไปได้ของกฎการเขียนโปรแกรมที่สิบของ Greenspun )
โปรดทราบว่า Haskell ให้การสนับสนุนเฉพาะระบบย่อยแบบไดนามิกที่มีObj
ประเภทซึ่งใช้ร่วมกับDynamic
ประเภทและคลาส Typeableเพื่อแทนที่Atomic
คลาสของเราซึ่งอนุญาตให้จัดเก็บค่าโดยพลการตามประเภทของพวกเขาและการบังคับกลับจากประเภทเหล่านั้นอย่างชัดเจน นั่นเป็นระบบที่คุณต้องใช้เพื่อทำงานกับโครงสร้าง Lisp cons ในลักษณะทั่วไปทั้งหมด
สิ่งที่คุณสามารถทำได้คือไปอีกทางหนึ่งและฝังระบบย่อยที่พิมพ์แบบคงที่ไว้ในภาษาที่พิมพ์แบบไดนามิกเป็นหลัก สิ่งนี้ช่วยให้คุณได้รับประโยชน์จากการตรวจสอบประเภทคงที่สำหรับส่วนต่างๆของโปรแกรมของคุณที่สามารถใช้ประโยชน์จากข้อกำหนดประเภทที่เข้มงวดมากขึ้น นี่ดูเหมือนจะเป็นแนวทางที่ใช้ในรูปแบบการตรวจสอบประเภทที่แม่นยำของ CMUCL แบบ จำกัดตัวอย่างเช่น
ในที่สุดมีความเป็นไปได้ที่จะมีระบบย่อยสองระบบแยกจากกันพิมพ์แบบไดนามิกและแบบคงที่ซึ่งใช้การเขียนโปรแกรมแบบสัญญาเพื่อช่วยนำทางในการเปลี่ยนระหว่างสองระบบ ด้วยวิธีนี้ภาษาสามารถรองรับการใช้งานของ Lisp ซึ่งการตรวจสอบประเภทคงที่จะเป็นอุปสรรคมากกว่าการช่วยเหลือเช่นเดียวกับการใช้ในกรณีที่การตรวจสอบประเภทคงที่จะเป็นประโยชน์ นี่เป็นแนวทางของTyped Racketดังที่คุณจะเห็นจากความคิดเห็นที่ตามมา
(Listof Integer)
(Listof Any)
เห็นได้ชัดว่าคุณสงสัยว่าหลังไม่มีประโยชน์เพราะคุณไม่รู้อะไรเกี่ยวกับประเภทนี้ แต่ใน TR คุณสามารถใช้งานได้ในภายหลัง(if (integer? x) ...)
และระบบจะรู้ว่าx
เป็นจำนวนเต็มในสาขาที่ 1
dynamic
ประเภทกำลังได้รับความนิยมในภาษาคงที่เป็นวิธีแก้ปัญหาชั่วคราวเพื่อให้ได้รับประโยชน์บางประการของภาษาที่พิมพ์แบบไดนามิกโดยการแลกเปลี่ยนตามปกติของค่าเหล่านี้จะถูกรวมไว้ในลักษณะที่ทำให้ประเภทต่างๆสามารถระบุตัวตนได้ แต่ที่นี่แร็กเก็ตที่พิมพ์ผิดก็ทำงานได้ดีมากในการทำให้สะดวกภายในภาษาตัวตรวจสอบประเภทใช้การเกิดขึ้นของเพรดิเคตเพื่อทราบข้อมูลเพิ่มเติมเกี่ยวกับประเภท ตัวอย่างเช่นดูตัวอย่างการพิมพ์ในหน้าแร็กเก็ตและดูว่าstring?
"ลด" รายการสตริงและหมายเลขลงในรายการสตริงอย่างไร
คำตอบของฉันโดยไม่ต้องมีระดับสูงของความเชื่อมั่นอาจจะ ตัวอย่างเช่นหากคุณดูภาษาเช่น SML และเปรียบเทียบกับ Lisp แกนการทำงานของแต่ละภาษาแทบจะเหมือนกัน ด้วยเหตุนี้ดูเหมือนว่าคุณจะไม่มีปัญหามากนักในการใช้การพิมพ์แบบคงที่กับแกนกลางของ Lisp (แอปพลิเคชันฟังก์ชันและค่าดั้งเดิม)
คำถามของคุณบอกได้เต็มปากเต็มคำและที่ฉันเห็นปัญหาบางอย่างเกิดขึ้นคือวิธีการโค้ดเป็นข้อมูล ประเภทมีอยู่ในระดับนามธรรมมากกว่านิพจน์ เสียงกระเพื่อมไม่มีความแตกต่างนี้ - โครงสร้างทุกอย่าง "แบน" ถ้าเราพิจารณานิพจน์บางอย่าง E: T (โดยที่ T เป็นตัวแทนของประเภทของมัน) จากนั้นเราจะถือว่านิพจน์นี้เป็นข้อมูล ol ธรรมดาแล้วประเภทของ T ตรงนี้คืออะไร? มันเป็นแบบ! ชนิดคือประเภทคำสั่งที่สูงกว่าดังนั้นเรามาพูดอะไรบางอย่างเกี่ยวกับสิ่งนั้นในรหัสของเรา:
E : T :: K
คุณอาจเห็นว่าฉันจะไปที่ไหนกับสิ่งนี้ ฉันแน่ใจว่าการแยกข้อมูลประเภทออกจากรหัสมันจะเป็นไปได้ที่จะหลีกเลี่ยงการอ้างอิงตัวเองประเภทนี้อย่างไรก็ตามจะทำให้ประเภทต่างๆไม่ "เสียงกระเพื่อม" ในรสชาติของมันมากนัก อาจมีหลายวิธีในการนี้แม้ว่าฉันจะไม่ชัดเจนว่าจะเป็นวิธีที่ดีที่สุด
แก้ไข: โอ้ด้วยการ googling เล็กน้อยฉันพบQiซึ่งดูเหมือนจะคล้ายกับ Lisp มากยกเว้นว่าจะพิมพ์แบบคงที่ บางทีอาจเป็นจุดเริ่มต้นที่ดีในการดูว่าพวกเขาทำการเปลี่ยนแปลงที่ใดเพื่อให้ได้การพิมพ์แบบคงที่