การเขียนโค้ด Code: An Introduction to theory and Practice of Modern Metaprogramming
เผยแพร่แล้ว: 2022-07-22เมื่อใดก็ตามที่ฉันนึกถึงวิธีที่ดีที่สุดในการอธิบายมาโคร ฉันจำโปรแกรม Python ที่ฉันเขียนเมื่อเริ่มเขียนโปรแกรมครั้งแรก ฉันไม่สามารถจัดระเบียบได้ตามต้องการ ฉันต้องเรียกใช้ฟังก์ชันที่แตกต่างกันเล็กน้อย และโค้ดก็ยุ่งยาก สิ่งที่ฉันค้นหา—แม้ว่าตอนนั้นฉันไม่รู้—คือ metaprogramming
metaprogramming (คำนาม)
เทคนิคใดๆ ก็ตามที่โปรแกรมสามารถปฏิบัติต่อโค้ดเสมือนเป็นข้อมูลได้
เราสามารถสร้างตัวอย่างที่แสดงให้เห็นถึงปัญหาเดียวกันกับที่ฉันประสบกับโครงการ Python โดยจินตนาการว่าเรากำลังสร้างส่วนหลังของแอปสำหรับเจ้าของสัตว์เลี้ยง การใช้เครื่องมือในห้องสมุด pet_sdk
เราเขียน Python เพื่อช่วยให้เจ้าของสัตว์เลี้ยงซื้ออาหารแมว:
หลังจากยืนยันว่ารหัสใช้งานได้แล้ว เราก็เริ่มใช้ตรรกะเดียวกันกับสัตว์เลี้ยงอีกสองประเภท (นกและสุนัข) นอกจากนี้เรายังเพิ่มคุณสมบัติในการจองนัดหมายสัตวแพทย์:
เป็นการดีที่จะย่อตรรกะที่ซ้ำซ้อนของ Snippet 2 ให้เป็นลูป ดังนั้นเราจึงเริ่มเขียนโค้ดใหม่ เราตระหนักได้อย่างรวดเร็วว่า เนื่องจากแต่ละฟังก์ชันมีชื่อต่างกัน เราจึงไม่สามารถระบุได้ว่าควรเรียกฟังก์ชันใด (เช่น book_bird_appointment
, book_cat_appointment
) ในลูปของเรา:
ลองนึกภาพเวอร์ชันเทอร์โบชาร์จของ Python ซึ่งเราสามารถเขียนโปรแกรมที่สร้างโค้ดสุดท้ายที่เราต้องการโดยอัตโนมัติ ซึ่งเราสามารถจัดการโปรแกรมของเราได้อย่างยืดหยุ่น ง่ายดาย และลื่นไหลราวกับว่ามันเป็นรายการ ข้อมูลในไฟล์ หรือใดๆ ประเภทข้อมูลทั่วไปอื่น ๆ หรืออินพุตโปรแกรม:
นี่คือตัวอย่างของ มาโคร ซึ่งมีให้บริการในภาษาต่างๆ เช่น Rust, Julia หรือ C เป็นต้น แต่ไม่ใช่ Python
ภาพจำลองนี้เป็นตัวอย่างที่ดีของการเขียนโปรแกรมที่สามารถปรับเปลี่ยนและจัดการโค้ดของตนเองได้ นี่คือการวาดมาโครอย่างแม่นยำ และเป็นหนึ่งในหลาย ๆ คำตอบสำหรับคำถามที่ใหญ่กว่า: เราจะให้โปรแกรมตรวจสอบโค้ดของตัวเอง ใช้เป็นข้อมูล แล้วดำเนินการวิปัสสนานั้นได้อย่างไร
โดยทั่วไปแล้ว เทคนิคทั้งหมดที่สามารถบรรลุวิปัสสนาดังกล่าวอยู่ภายใต้คำว่า "โปรแกรมเมตาโปรแกรมมิ่ง" แบบครอบคลุม Metaprogramming เป็นฟิลด์ย่อยที่สมบูรณ์ในการออกแบบภาษาโปรแกรม และสามารถตรวจสอบย้อนกลับไปยังแนวคิดที่สำคัญอย่างหนึ่ง: รหัสเป็นข้อมูล
ภาพสะท้อน: ในการป้องกันของ Python
คุณอาจชี้ให้เห็นว่าแม้ว่า Python จะไม่ให้การสนับสนุนมาโคร แต่ก็มีวิธีอื่นๆ มากมายในการเขียนโค้ดนี้ ตัวอย่างเช่น ที่นี่เราใช้เมธอด isinstance()
เพื่อระบุคลาสที่ตัวแปร animal
ของเราเป็นตัวอย่างและเรียกใช้ฟังก์ชันที่เหมาะสม:
เราเรียกการ สะท้อน เมตาโปรแกรมมิงประเภทนี้ และเราจะกลับมาพูดถึงในภายหลัง โค้ดของ Snippet 5 นั้นยังค่อนข้างยุ่งยากอยู่เล็กน้อย แต่สำหรับโปรแกรมเมอร์จะเขียนได้ง่ายกว่า Snippet 2 ซึ่งเราได้ทวนตรรกะสำหรับสัตว์แต่ละตัวที่อยู่ในรายการ
ท้าทาย
ใช้เมธอด getattr
แก้ไขโค้ดก่อนหน้าเพื่อเรียกใช้ฟังก์ชัน order_*_food
และ book_*_appointment
ที่เหมาะสมแบบไดนามิก เนื้อหานี้ทำให้โค้ดอ่านได้น้อยลง แต่ถ้าคุณรู้จัก Python ดี ก็ควรพิจารณาว่าคุณจะใช้ getattr
แทนฟังก์ชัน isinstance
ได้อย่างไร และทำให้โค้ดง่ายขึ้น
Homoiconicity: ความสำคัญของ Lisp
ภาษาโปรแกรมบางภาษา เช่น Lisp นำแนวคิดของ metaprogramming ไปสู่อีกระดับหนึ่งผ่าน homoiconicity
homoiconicity (คำนาม)
คุณสมบัติของภาษาการเขียนโปรแกรมโดยที่ไม่มีความแตกต่างระหว่างโค้ดกับข้อมูลที่โปรแกรมทำงานอยู่
Lisp สร้างขึ้นในปี 1958 เป็นภาษา homoiconic ที่เก่าแก่ที่สุดและเป็นภาษาการเขียนโปรแกรมระดับสูงที่เก่าแก่เป็นอันดับสอง ได้ชื่อมาจาก “LISt Processor” Lisp คือการปฏิวัติในการประมวลผลซึ่งกำหนดรูปแบบการใช้และตั้งโปรแกรมคอมพิวเตอร์อย่างลึกซึ้ง เป็นการยากที่จะพูดเกินจริงว่า Lisp มีอิทธิพลต่อการเขียนโปรแกรมอย่างไรโดยพื้นฐานและชัดเจน
Emacs เขียนด้วย Lisp ซึ่งเป็นภาษาคอมพิวเตอร์เพียงภาษาเดียวที่สวยงาม นีล สตีเฟนสัน
Lisp ถูกสร้างขึ้นเพียงหนึ่งปีหลังจาก FORTRAN ในยุคของการ์ดเจาะรูและคอมพิวเตอร์ทางการทหารที่เต็มห้อง ทุกวันนี้โปรแกรมเมอร์ยังคงใช้ Lisp เพื่อเขียนแอปพลิเคชั่นใหม่ที่ทันสมัย John McCarthy ผู้สร้างหลักของ Lisp เป็นผู้บุกเบิกด้าน AI หลายปีที่ผ่านมา Lisp เป็นภาษาของ AI โดยนักวิจัยให้รางวัลความสามารถในการเขียนโค้ดของตนเองใหม่แบบไดนามิก การวิจัย AI ในปัจจุบันมีศูนย์กลางอยู่ที่โครงข่ายประสาทเทียมและแบบจำลองทางสถิติที่ซับซ้อน แทนที่จะเป็นรหัสการสร้างตรรกะประเภทนั้น อย่างไรก็ตาม การวิจัยเกี่ยวกับ AI โดยใช้ Lisp โดยเฉพาะอย่างยิ่งการวิจัยที่ทำขึ้นในยุค 60 และ 70 ที่ MIT และ Stanford ได้สร้างสาขาวิชาดังที่เรารู้จัก และอิทธิพลมหาศาลยังคงมีอยู่
การถือกำเนิดของ Lisp ทำให้โปรแกรมเมอร์รุ่นก่อน ๆ มองเห็นความเป็นไปได้ในการคำนวณเชิงปฏิบัติของสิ่งต่าง ๆ เช่น การเรียกซ้ำ ฟังก์ชันระดับสูง และรายการที่เชื่อมโยงเป็นครั้งแรก นอกจากนี้ยังแสดงให้เห็นถึงพลังของภาษาการเขียนโปรแกรมที่สร้างขึ้นจากแนวคิดของแคลคูลัสแลมบ์ดา
แนวคิดเหล่านี้จุดประกายให้เกิดการระเบิดในการออกแบบภาษาโปรแกรม และตามที่ Edsger Dijkstra หนึ่งในชื่อที่ยิ่งใหญ่ที่สุดในสาขาวิทยาการคอมพิวเตอร์กล่าวไว้ “ […] ช่วยเพื่อนมนุษย์ที่มีพรสวรรค์ที่สุดของเราหลายคนในการคิดความคิดที่เป็นไปไม่ได้ก่อนหน้านี้”
ตัวอย่างนี้แสดงโปรแกรม Lisp อย่างง่าย (และเทียบเท่าในไวยากรณ์ Python ที่คุ้นเคย) ที่กำหนดฟังก์ชัน "แฟกทอเรียล" ที่คำนวณแฟกทอเรียลซ้ำๆ ของอินพุตและการเรียกใช้ฟังก์ชันที่มีอินพุต "7":
Lisp | Python |
---|---|
( defun factorial ( n ) ( if ( = n 1 ) 1 ( * n ( factorial ( - n 1 ))))) ( print ( factorial 7 )) | |
รหัสเป็นข้อมูล
แม้จะเป็นหนึ่งในนวัตกรรมที่สร้างผลกระทบและเป็นผลสืบเนื่องมากที่สุดของ Lisp แต่ homoiconicity ซึ่งแตกต่างจากการเรียกซ้ำและแนวคิดอื่น ๆ อีกมากมายที่ Lisp เป็นผู้บุกเบิก แต่ไม่ได้ทำให้มันเป็นภาษาการเขียนโปรแกรมส่วนใหญ่ในปัจจุบัน
ตารางต่อไปนี้เปรียบเทียบฟังก์ชัน homoiconic ที่ส่งคืนโค้ดทั้งใน Julia และ Lisp Julia เป็นภาษา homoiconic ซึ่งคล้ายกับภาษาระดับสูงที่คุณอาจคุ้นเคย (เช่น Python, Ruby) ในหลายๆ ด้าน
ส่วนสำคัญของไวยากรณ์ในแต่ละตัวอย่างคืออักขระ อ้างอิง Julia ใช้ a :
(เครื่องหมายทวิภาค) เพื่ออ้างอิง ในขณะที่ Lisp ใช้ '
(เครื่องหมายคำพูดเดียว):
จูเลีย | Lisp |
---|---|
function function_that_returns_code() return :(x + 1 ) end | |
ในทั้งสองตัวอย่าง เครื่องหมายคำพูดข้างนิพจน์หลัก ( (x + 1)
หรือ (+ x 1)
) จะแปลงจากโค้ดที่จะถูกประเมินโดยตรงเป็นนิพจน์นามธรรมที่เราจัดการได้ ฟังก์ชันส่งคืนโค้ด—ไม่ใช่สตริงหรือข้อมูล หากเราต้องเรียกใช้ฟังก์ชันของเราและเขียน print(function_that_returns_code())
Julia จะพิมพ์โค้ดที่เป็นสตริงเป็น x+1
(และเทียบเท่ากับ Lisp) ในทางกลับกัน ถ้าไม่มี :
(หรือ '
ใน Lisp) เราจะได้รับข้อผิดพลาดที่ x
ไม่ได้กำหนดไว้
กลับไปที่ตัวอย่าง Julia ของเราและขยาย:
ฟังก์ชัน eval
สามารถใช้เพื่อเรียกใช้โค้ดที่เราสร้างจากที่อื่นในโปรแกรม โปรดทราบว่าค่าที่พิมพ์ออกมาจะขึ้นอยู่กับคำจำกัดความของตัวแปร x
หากเราพยายาม eval
ที่สร้างขึ้นในบริบทที่ไม่ได้กำหนด x
เราจะได้รับข้อผิดพลาด
Homoiconicity เป็นโปรแกรม metaprogramming ที่ทรงพลัง สามารถปลดล็อกกระบวนทัศน์การเขียนโปรแกรมที่แปลกใหม่และซับซ้อน ซึ่งโปรแกรมสามารถปรับตัวได้ทันที สร้างรหัสเพื่อให้พอดีกับปัญหาเฉพาะโดเมนหรือรูปแบบข้อมูลใหม่ที่พบ
ใช้กรณีของ WolframAlpha ซึ่งภาษา Wolfram แบบ homoiconic สามารถสร้างโค้ดเพื่อปรับให้เข้ากับปัญหาที่หลากหลายอย่างไม่น่าเชื่อ คุณสามารถถาม WolframAlpha ได้ว่า "GDP ของนครนิวยอร์กหารด้วยประชากรของอันดอร์ราเป็นเท่าใด" และได้รับการตอบสนองเชิงตรรกะอย่างน่าทึ่ง
ดูเหมือนไม่น่าเป็นไปได้ที่ทุกคนจะคิดรวมการคำนวณที่คลุมเครือและไร้จุดหมายนี้ไว้ในฐานข้อมูล แต่ Wolfram ใช้เมตาโปรแกรมมิ่งและกราฟความรู้ทางออนโทโลยีเพื่อเขียนโค้ดแบบทันทีเพื่อตอบคำถามนี้
สิ่งสำคัญคือต้องเข้าใจความยืดหยุ่นและพลังที่ Lisp และภาษา homoiconic อื่นๆ มีให้ ก่อนที่เราจะลงลึกในรายละเอียดเพิ่มเติม ลองพิจารณาตัวเลือก metaprogramming บางส่วนที่คุณมี:
คำนิยาม | ตัวอย่าง | หมายเหตุ | |
---|---|---|---|
ความคล้ายคลึงกัน | ลักษณะภาษาที่รหัสเป็นข้อมูล "ชั้นหนึ่ง" เนื่องจากไม่มีการแยกระหว่างรหัสและข้อมูล ทั้งสองสามารถใช้แทนกันได้ |
| Lisp มีภาษาอื่นๆ ในตระกูล Lisp เช่น Scheme, Racket และ Clojure |
มาโคร | คำสั่ง ฟังก์ชัน หรือนิพจน์ที่รับโค้ดเป็นอินพุต และส่งคืนโค้ดเป็นเอาต์พุต |
| (ดูหมายเหตุถัดไปเกี่ยวกับมาโครของ C) |
คำสั่งพรีโปรเซสเซอร์ (หรือพรีคอมไพเลอร์) | ระบบที่รับโปรแกรมเป็นอินพุตและส่งคืนเวอร์ชันที่เปลี่ยนแปลงของโปรแกรมเป็นเอาต์พุตตามคำสั่งที่รวมอยู่ในโค้ด |
| มาโครของ C ถูกใช้งานโดยใช้ระบบพรีโปรเซสเซอร์ของ C แต่ทั้งสองเป็นแนวคิดที่แยกจากกัน ความแตกต่างของแนวคิดหลักระหว่างมาโครของ C (ซึ่งเราใช้ #define คำสั่งตัวประมวลผลล่วงหน้า) และรูปแบบอื่น ๆ ของคำสั่งตัวประมวลผลล่วงหน้า C (เช่น #if และ #ifndef ) คือการที่เราใช้มาโครเพื่อสร้างโค้ดในขณะที่ใช้ที่ไม่ใช่ #define คำสั่งพรีโปรเซสเซอร์เพื่อคอมไพล์โค้ดอื่นตามเงื่อนไข ทั้งสองมีความเกี่ยวข้องกันอย่างใกล้ชิดในภาษา C และในภาษาอื่นบางภาษา แต่เป็น metaprogramming ประเภทต่างๆ |
การสะท้อน | ความสามารถของโปรแกรมในการตรวจสอบ แก้ไข และไตร่ตรองรหัสของตนเอง |
| การสะท้อนสามารถเกิดขึ้นได้ในเวลาคอมไพล์หรือขณะรันไทม์ |
ยาสามัญ | ความสามารถในการเขียนโค้ดที่ถูกต้องสำหรับหลายประเภทที่แตกต่างกัน หรือสามารถใช้ได้ในหลายบริบท แต่เก็บไว้ในที่เดียว เราสามารถกำหนดบริบทที่โค้ดนั้นใช้ได้ไม่ว่าจะโดยชัดแจ้งหรือโดยปริยาย | ข้อมูลทั่วไปในรูปแบบเทมเพลต:
ความหลากหลายทางพารามิเตอร์:
| โปรแกรมทั่วไปเป็นหัวข้อที่กว้างกว่า metaprogramming ทั่วไป และเส้นแบ่งระหว่างทั้งสองไม่ได้กำหนดไว้อย่างชัดเจน ในมุมมองของผู้เขียนรายนี้ ระบบประเภทพารามิเตอร์จะนับเป็น metaprogramming หากเป็นภาษาที่พิมพ์แบบสแตติกเท่านั้น |
มาดูตัวอย่างเชิงปฏิบัติของ homoiconicity, macros, preprocessor directives, reflection และ generics ที่เขียนในภาษาการเขียนโปรแกรมต่างๆ:
มาโคร (เช่นเดียวกับใน Snippet 11) กำลังได้รับความนิยมอีกครั้งในภาษาการเขียนโปรแกรมรุ่นใหม่ ในการพัฒนาสิ่งเหล่านี้ให้ประสบความสำเร็จ เราต้องพิจารณาหัวข้อสำคัญ: สุขอนามัย
มาโครที่ถูกสุขอนามัยและไม่ถูกสุขลักษณะ
โค้ดที่ "ถูกสุขอนามัย" หรือ "ไม่ถูกสุขลักษณะ" หมายความว่าอย่างไร เพื่อความชัดเจน มาดูมาโคร Rust ซึ่งสร้างอินสแตนซ์โดย macro_rules!
การทำงาน. ตามชื่อที่สื่อถึง macro_rules!
สร้างรหัสตามกฎที่เรากำหนด ในกรณีนี้ เราได้ตั้งชื่อมาโครของเราว่า my_macro
และกฎคือ “สร้างบรรทัดของโค้ด let x = $n
” โดยที่ n
คืออินพุตของเรา:
เมื่อเราขยายมาโครของเรา (เรียกใช้มาโครเพื่อแทนที่การเรียกใช้ด้วยรหัสที่สร้าง) เราคาดว่าจะได้รับสิ่งต่อไปนี้:
ดูเหมือนว่ามาโครของเราได้กำหนดตัวแปร x
ใหม่ให้เท่ากับ 3 ดังนั้นเราอาจคาดหวังให้โปรแกรมพิมพ์ 3
อย่างสมเหตุสมผล อันที่จริงมันพิมพ์ 5
! น่าประหลาดใจ? ใน Rust, macro_rules!
ถูกสุขลักษณะสำหรับตัวระบุ ดังนั้นจะไม่ "จับ" ตัวระบุที่อยู่นอกขอบเขต ในกรณีนี้ ตัวระบุคือ x
ถ้ามันถูกจับโดยมาโคร มันจะเท่ากับ 3
สุขอนามัย (นาม)
คุณสมบัติที่รับประกันว่าการขยายของมาโครจะไม่จับตัวระบุหรือสถานะอื่นๆ จากนอกขอบเขตของมาโคร ระบบมาโครและระบบมาโครที่ไม่มีคุณสมบัตินี้เรียกว่า ไม่ถูกสุขลักษณะ
สุขอนามัยในมาโครเป็นหัวข้อที่ค่อนข้างขัดแย้งในหมู่นักพัฒนา ผู้เสนอยืนยันว่าหากไม่มีสุขอนามัย ทั้งหมดนั้นง่ายเกินไปที่จะปรับเปลี่ยนพฤติกรรมของโค้ดอย่างละเอียดโดยไม่ได้ตั้งใจ ลองนึกภาพมาโครที่มีความซับซ้อนมากกว่า Snippet 13 ที่ใช้ในโค้ดที่ซับซ้อนซึ่งมีตัวแปรและตัวระบุอื่นๆ มากมาย จะเกิดอะไรขึ้นถ้ามาโครนั้นใช้ตัวแปรเดียวกับโค้ดของคุณ และคุณไม่ได้สังเกต
ไม่ใช่เรื่องแปลกที่นักพัฒนาจะใช้แมโครจากไลบรารีภายนอกโดยไม่ต้องอ่านซอร์สโค้ด นี่เป็นเรื่องปกติโดยเฉพาะอย่างยิ่งในภาษาใหม่ ๆ ที่ให้การสนับสนุนมาโคร (เช่น Rust และ Julia):
มาโครที่ไม่ถูกสุขลักษณะใน C จะรวบรวม website
ตัวระบุและเปลี่ยนแปลงค่าของเว็บไซต์ แน่นอนว่าการดักจับตัวระบุไม่เป็นอันตราย เป็นเพียงผลที่ตามมาโดยบังเอิญจากการใช้มาโคร
ดังนั้นมาโครที่ถูกสุขลักษณะนั้นดีและมาโครที่ไม่ถูกสุขอนามัยก็แย่ใช่ไหม น่าเสียดายที่มันไม่ง่ายอย่างนั้น มีกรณีที่ชัดเจนที่จะทำให้มาโครที่ถูกสุขอนามัยจำกัดเรา บางครั้ง การดักจับตัวระบุก็มีประโยชน์ มาทบทวน Snippet 2 กันอีกครั้ง ซึ่งเราใช้ pet_sdk
เพื่อให้บริการสัตว์เลี้ยงสามประเภท รหัสเดิมของเราเริ่มต้นเช่นนี้:
คุณจะจำได้ว่า Snippet 3 เป็นความพยายามในการย่อตรรกะซ้ำๆ ของ Snippet 2 ให้เป็นลูปรวมทุกอย่าง แต่ถ้าโค้ดของเราขึ้นอยู่กับตัวระบุ cats
and dogs
และเราต้องการเขียนสิ่งต่อไปนี้:
Snippet 16 ค่อนข้างเรียบง่าย แต่ลองนึกภาพกรณีที่เราต้องการให้มาโครเขียนโค้ดส่วนที่กำหนด 100% มาโครที่ถูกสุขอนามัยอาจมีข้อจำกัดในกรณีเช่นนี้
แม้ว่าการดีเบตแบบมหภาคที่ถูกสุขอนามัยและไม่ถูกสุขลักษณะอาจมีความซับซ้อน แต่ข่าวดีก็คือ คุณไม่จำเป็นต้องมีจุดยืน ภาษาที่คุณใช้เป็นตัวกำหนดว่ามาโครของคุณจะถูกสุขอนามัยหรือไม่ถูกสุขลักษณะ ดังนั้น พึงระลึกไว้เสมอว่าเมื่อใช้มาโคร
มาโครสมัยใหม่
มาโครกำลังมีช่วงเวลาเล็กน้อยในขณะนี้ เป็นเวลานาน ที่จุดเน้นของภาษาโปรแกรมที่จำเป็นสมัยใหม่ได้เปลี่ยนจากมาโครที่เป็นส่วนหลักของฟังก์ชันการทำงาน โดยละทิ้งภาษาเหล่านี้ไปเป็นโปรแกรม metaprogramming ประเภทอื่นๆ
ภาษาที่โปรแกรมเมอร์ใหม่ได้รับการสอนในโรงเรียน (เช่น Python และ Java) บอกพวกเขาว่าสิ่งที่พวกเขาต้องการคือการไตร่ตรองและคำทั่วไป
เมื่อเวลาผ่านไป เมื่อภาษาสมัยใหม่เหล่านี้ได้รับความนิยม มาโครก็เชื่อมโยงกับไวยากรณ์ตัวประมวลผลล่วงหน้า C และ C++ ที่น่ากลัว—หากโปรแกรมเมอร์รับรู้ถึงสิ่งเหล่านี้เลย
อย่างไรก็ตาม ด้วยการถือกำเนิดของ Rust และ Julia เทรนด์ได้เปลี่ยนกลับไปเป็นมาโคร Rust และ Julia เป็นภาษาสองภาษาที่ทันสมัย เข้าถึงได้ และใช้กันอย่างแพร่หลาย ซึ่งได้กำหนดนิยามใหม่และเผยแพร่แนวคิดของมาโครด้วยแนวคิดใหม่และสร้างสรรค์ สิ่งนี้น่าตื่นเต้นเป็นพิเศษใน Julia ซึ่งดูพร้อมที่จะแทนที่ Python และ R เป็นภาษาอเนกประสงค์ "รวมแบตเตอรี่" ที่ใช้งานง่าย
เมื่อเราดู pet_sdk
ผ่านแว่นตา “TurboPython” ครั้งแรก สิ่งที่เราต้องการจริงๆ ก็คือ Julia มาเขียน Snippet 2 ใหม่ใน Julia โดยใช้ homoiconicity และเครื่องมือ metaprogramming อื่น ๆ ที่มีให้:
มาแยกย่อย Snippet 17:
- เราทำซ้ำผ่านสามสิ่งอันดับ สิ่งแรกคือ
("cat", :clean_litterbox)
ดังนั้นตัวแปรpet
จึงถูกกำหนดให้กับ"cat"
และตัวแปรcare_fn
ถูกกำหนดให้กับสัญลักษณ์ที่ยกมา:clean_litterbox
- เราใช้ฟังก์ชัน
Meta.parse
เพื่อแปลงสตริงเป็นExpression
เพื่อให้เราสามารถประเมินเป็นโค้ดได้ ในกรณีนี้ เราต้องการใช้พลังของการแก้ไขสตริง โดยที่เราสามารถใส่สตริงหนึ่งไปยังอีกสตริงหนึ่ง เพื่อกำหนดฟังก์ชันที่จะเรียกใช้ - เราใช้ฟังก์ชัน
eval
เพื่อรันโค้ดที่เรากำลังสร้าง@eval begin… end
เป็นอีกวิธีหนึ่งในการเขียนeval(...)
เพื่อหลีกเลี่ยงการพิมพ์รหัสซ้ำ ภายในบล็อก@eval
คือโค้ดที่เรากำลังสร้างแบบไดนามิกและทำงานอยู่
ระบบ metaprogramming ของ Julia ทำให้เราเป็นอิสระอย่างแท้จริงในการแสดงสิ่งที่เราต้องการในแบบที่เราต้องการ เราอาจใช้วิธีอื่นๆ ได้หลายวิธี รวมถึงการไตร่ตรอง (เช่น Python ใน Snippet 5) นอกจากนี้เรายังสามารถเขียนฟังก์ชันมาโครที่สร้างรหัสสำหรับสัตว์ตัวใดตัวหนึ่งอย่างชัดเจน หรือเราอาจสร้างรหัสทั้งหมดเป็นสตริงและใช้ Meta.parse
หรือวิธีการใด ๆ ที่ผสมผสานกัน
Beyond Julia: ระบบ Metaprogramming สมัยใหม่อื่นๆ
Julia อาจเป็นหนึ่งในตัวอย่างที่น่าสนใจและน่าสนใจที่สุดของระบบมาโครสมัยใหม่ แต่ก็ไม่ได้เป็นเพียงระบบเดียว สนิมก็มีบทบาทสำคัญในการนำมาโครมาสู่โปรแกรมเมอร์อีกครั้ง
ใน Rust มาโครมีจุดศูนย์กลางมากกว่า Julia มาก แม้ว่าเราจะไม่ทำการสำรวจทั้งหมดที่นี่ ด้วยเหตุผลหลายประการ คุณไม่สามารถเขียน Rust สำนวนได้โดยไม่ต้องใช้มาโคร อย่างไรก็ตาม ใน Julia คุณสามารถเลือกที่จะเพิกเฉยต่อระบบ homoiconicity และระบบมาโครได้อย่างสมบูรณ์
จากผลโดยตรงของความเป็นศูนย์กลางนั้น ระบบนิเวศของ Rust ได้นำมาโครมาใช้อย่างแท้จริง สมาชิกของชุมชนได้สร้างไลบรารีที่ยอดเยี่ยมอย่างเหลือเชื่อ การพิสูจน์แนวคิด และฟีเจอร์ด้วยมาโคร รวมถึงเครื่องมือที่สามารถทำให้เป็นอนุกรมและดีซีเรียลไลซ์ข้อมูล สร้าง SQL โดยอัตโนมัติ หรือแม้แต่แปลงคำอธิบายประกอบที่เหลือในโค้ดเป็นภาษาการเขียนโปรแกรมอื่น ทั้งหมดนี้สร้างขึ้นในโค้ดที่ รวบรวมเวลา
แม้ว่า metaprogramming ของ Julia อาจมีความชัดเจนและเป็นอิสระมากกว่า Rust อาจเป็นตัวอย่างที่ดีที่สุดของภาษาสมัยใหม่ที่ยกระดับ metaprogramming เนื่องจากมีจุดเด่นอย่างมากตลอดทั้งภาษา
ดวงตาสู่อนาคต
ตอนนี้เป็นเวลาที่น่าเหลือเชื่อที่จะสนใจในภาษาการเขียนโปรแกรม วันนี้ฉันสามารถเขียนแอปพลิเคชันใน C ++ และเรียกใช้ในเว็บเบราว์เซอร์หรือเขียนแอปพลิเคชันใน JavaScript เพื่อทำงานบนเดสก์ท็อปหรือโทรศัพท์ อุปสรรคในการเข้าไม่เคยลดลง และโปรแกรมเมอร์ใหม่มีข้อมูลอยู่ใกล้แค่เพียงปลายนิ้วสัมผัสอย่างที่ไม่เคยมีมาก่อน
ในโลกของทางเลือกและเสรีภาพของโปรแกรมเมอร์นี้ เรามีสิทธิ์ใช้ภาษาที่ทันสมัยและสมบูรณ์มากขึ้น ซึ่งคัดสรรคุณลักษณะและแนวคิดจากประวัติศาสตร์ของวิทยาการคอมพิวเตอร์และภาษาการเขียนโปรแกรมรุ่นก่อนๆ เป็นเรื่องที่น่าตื่นเต้นที่ได้เห็นมาโครหยิบขึ้นมาและปัดฝุ่นออกจากคลื่นแห่งการพัฒนานี้ ฉันแทบรอไม่ไหวที่จะได้เห็นนักพัฒนารุ่นใหม่ๆ จะทำอย่างไรในขณะที่ Rust และ Julia แนะนำให้พวกเขารู้จักกับมาโคร จำไว้ว่า “โค้ดเป็นข้อมูล” เป็นมากกว่าวลีติดปาก เป็นอุดมการณ์หลักที่ต้องคำนึงถึงเมื่อพูดถึง metaprogramming ในชุมชนออนไลน์หรือสภาพแวดล้อมทางวิชาการ
'รหัสเป็นข้อมูล' เป็นมากกว่าวลีติดปาก
ประวัติ 64 ปีของ Metaprogramming เป็นส่วนสำคัญในการพัฒนาโปรแกรมมิ่งดังที่เราทราบกันในปัจจุบัน แม้ว่านวัตกรรมและประวัติศาสตร์ที่เราสำรวจเป็นเพียงมุมหนึ่งของเทพนิยายเกี่ยวกับการเขียนโปรแกรมเมตา แต่ก็แสดงให้เห็นถึงพลังที่แข็งแกร่งและยูทิลิตี้ของโปรแกรมเมตาโปรแกรมที่ทันสมัย