เมื่อใดที่จะใช้มาโครหรือไม่ใช้ [ปิด]


12

เมื่อใดที่ฉันควรใช้มาโครในโปรแกรมหรือไม่

คำถามนี้ได้รับแรงบันดาลใจจากคำตอบที่ให้ข้อมูลโดย @tarsius ฉันเชื่อว่าหลายคนมีประสบการณ์ที่ยอดเยี่ยมในการแบ่งปันผู้อื่น ฉันหวังว่าคำถามนี้กลายเป็นหนึ่งในคำถามอัตนัยที่ดีและช่วยโปรแกรมเมอร์ของ Emacs

คำตอบ:


13

หลักการง่ายๆ

ใช้มาโครสำหรับไวยากรณ์แต่ไม่ต้องมีความหมาย

มันเป็นเรื่องดีที่จะใช้แมโครสำหรับน้ำตาลประโยค แต่มันเป็นความคิดที่ดีที่จะใส่ตรรกะลงในร่างกายของแมโครโดยไม่คำนึงว่ามันจะอยู่ในแมโครหรือในการขยายตัว หากคุณรู้สึกว่าจำเป็นต้องทำเช่นนั้นให้เขียนฟังก์ชันแทนและ "มาโครคู่หู" สำหรับน้ำตาลประโยค พูดง่ายๆก็คือถ้าคุณมีletมาโครอยู่แล้วคุณกำลังมองหาปัญหา

ทำไม?

มาโครมีข้อเสียมากมาย:

  • พวกเขาขยายเวลาการรวบรวมและดังนั้นจะต้องเขียนอย่างระมัดระวังเพื่อรักษาความเข้ากันได้ไบนารีข้ามหลายรุ่น ตัวอย่างเช่นการลบฟังก์ชั่นหรือตัวแปรที่ใช้ในการขยายตัวของแมโครจะทำลายรหัสไบต์ของแพ็คเกจที่ขึ้นต่อกันทั้งหมดที่ใช้มาโครนี้ กล่าวอีกนัยหนึ่งถ้าคุณเปลี่ยนการใช้งานของแมโครในวิธีที่เข้ากันไม่ได้แพคเกจที่อ้างถึงทั้งหมดต้องถูก recompiled และความเข้ากันได้ของไบนารีไม่ชัดเจนเสมอไป ...
  • คุณจะต้องดูแลรักษาเพื่อการประเมินผลที่ใช้งานง่าย มันเป็นเรื่องง่ายที่จะเขียนแมโครที่ประเมินฟอร์มบ่อยเกินไปหรือผิดเวลาหรือผิดที่ ฯลฯ
  • คุณต้องระวังซีแมนทิกส์ที่มีการเชื่อมโยง : แมโครสืบทอดบริบทการเชื่อมโยงจากไซต์ส่วนขยายซึ่งหมายความว่าคุณไม่ทราบว่ามีตัวแปรตัวแปรศัพท์อยู่หรือไม่และการผูกศัพท์มีอยู่ด้วยหรือไม่ แมโครต้องทำงานร่วมกับซีแมนทิกส์รวม
  • ที่เกี่ยวข้องกับการนี้คุณต้องระวังเมื่อเขียนแมโครที่สร้างปิด โปรดจำไว้ว่าคุณได้รับการเชื่อมโยงจากไซต์ส่วนขยายไม่ใช่จากคำศัพท์คำศัพท์ดังนั้นให้ดูสิ่งที่คุณปิดและวิธีการสร้างการปิด (โปรดจำไว้ว่าคุณไม่ทราบว่าการเชื่อมคำศัพท์มีการใช้งานที่ไซต์ส่วนขยายหรือไม่ คุณยังมีการปิด)

1
หมายเหตุเกี่ยวกับการขยายคำศัพท์และการขยายตัวของแมโครนั้นน่าสนใจเป็นพิเศษ ขอขอบคุณคุณ lunaryorn (ผมไม่เคยเปิดใช้งานคำศัพท์ที่มีผลผูกพันสำหรับรหัสใด ๆ ที่ผมเคยเขียนไว้แล้วดังนั้นจึงไม่เกิดความขัดแย้งที่ฉันเคยคิดเกี่ยวกับ.)
Phils

6

จากประสบการณ์การอ่านการดีบักและการแก้ไขโค้ด Elisp ของฉันและของผู้อื่นฉันแนะนำให้หลีกเลี่ยงมาโครให้มากที่สุด

สิ่งที่ชัดเจนเกี่ยวกับมาโครคือพวกเขาไม่ได้ประเมินข้อโต้แย้ง ซึ่งหมายความว่าหากคุณมีการแสดงออกที่ซับซ้อนห่อในแมโครคุณไม่มีความคิดว่าจะได้รับการประเมินหรือไม่และในบริบทใดเว้นแต่คุณจะค้นหาคำนิยามแมโคร นี่อาจไม่ใช่เรื่องใหญ่สำหรับตัวคุณเองเนื่องจากคุณรู้คำจำกัดความของมาโครของคุณ (ในปัจจุบันอาจจะไม่กี่ปีต่อมา)

ข้อเสียนี้ใช้ไม่ได้กับฟังก์ชั่น: args ทั้งหมดจะถูกประเมินก่อนการเรียกใช้ฟังก์ชัน ซึ่งหมายความว่าคุณสามารถสร้างความซับซ้อน eval ในสถานการณ์การดีบักที่ไม่รู้จัก คุณสามารถข้ามคำจำกัดความของฟังก์ชั่นที่คุณไม่รู้จักได้ตราบใดที่พวกเขาส่งคืนข้อมูลที่ตรงไปตรงมาและคุณคิดว่าพวกเขาไม่ได้สัมผัสกับสถานะโลก

หากต้องการใส่รหัสใหม่ด้วยวิธีอื่นแมโครจะกลายเป็นส่วนหนึ่งของภาษา หากคุณกำลังอ่านโค้ดที่มีมาโครที่ไม่รู้จักคุณเกือบจะอ่านโค้ดในภาษาที่คุณไม่รู้

ข้อยกเว้นการจองข้างต้น:

แมโครมาตรฐานชอบpush, pop, dolistทำจริงๆมากสำหรับคุณและเป็นที่รู้จักกันเขียนโปรแกรมอื่น ๆ พวกมันเป็นส่วนหนึ่งของข้อมูลจำเพาะภาษา แต่มันเป็นไปไม่ได้ที่จะสร้างแมโครของคุณให้เป็นมาตรฐาน ไม่เพียง แต่จะยากที่จะคิดหาสิ่งที่ง่ายและมีประโยชน์อย่างตัวอย่างด้านบน แต่ยิ่งยากขึ้นคือการโน้มน้าวให้โปรแกรมเมอร์ทุกคนเรียนรู้

นอกจากนี้ผมไม่ทราบเหมือนแมโครsave-selected-window: พวกเขาเก็บและเรียกคืนรัฐและประเมินผลการศึกษาของพวกเขา args ทางเดียวกันprognไม่ ตรงไปตรงมาสวยทำให้รหัสง่ายขึ้น

ตัวอย่างของแมโครตกลงอื่นได้สิ่งที่ต้องการหรือdefhydra use-packageมาโครเหล่านี้มีภาษามินิเป็นของตัวเอง prognและพวกเขารักษาข้อมูลง่ายทั้งยังคงทำหน้าที่เหมือนหรือ รวมทั้งพวกมันมักจะอยู่ที่ระดับบนสุดดังนั้นจึงไม่มีตัวแปรใน call stack สำหรับอาร์กิวเมนต์ของแมโครที่ต้องพึ่งพาทำให้ง่ายขึ้นเล็กน้อย

ตัวอย่างของมาโครที่แย่ในความคิดของฉันคือmagit-section-actionและmagit-section-caseใน Magit อันแรกถูกเอาออกไปแล้ว แต่อันที่สองยังคงอยู่


4

ฉันจะทื่อ: ฉันไม่เข้าใจว่า "ใช้สำหรับไวยากรณ์ไม่ใช่เพื่อความหมาย" นี่คือเหตุผลที่ส่วนหนึ่งของคำตอบนี้จะจัดการกับคำตอบของLunaryonและส่วนอื่น ๆ จะเป็นความพยายามของฉันในการตอบคำถามเดิม

เมื่อคำว่า "ซีแมนทิกส์" ใช้ในการเขียนโปรแกรมมันหมายถึงวิธีที่ผู้เขียนภาษาเลือกที่จะตีความภาษาของพวกเขา โดยทั่วไปแล้วสิ่งนี้จะลดลงเป็นชุดของสูตรที่แปลงกฎไวยากรณ์ของภาษาเป็นกฎอื่น ๆ ที่ผู้อ่านคาดว่าจะคุ้นเคย ตัวอย่างเช่น Plotkin ในหนังสือของเขาApproach Approach ต่อ Operational Semanticsใช้ภาษาที่ได้จากตรรกะเพื่ออธิบายว่าไวยากรณ์ที่ประเมินได้คืออะไร (หมายถึงอะไรในการรันโปรแกรม) กล่าวอีกนัยหนึ่งถ้าไวยากรณ์คือสิ่งที่ก่อให้เกิดภาษาการเขียนโปรแกรมในบางระดับแล้วมันจะต้องมีความหมายบางอย่างมิฉะนั้นมันคือ ... ไม่ใช่ภาษาโปรแกรม

ทีนี้เรามาลืมคำจำกัดความที่เป็นทางการกันสักหน่อยมันสำคัญที่คุณต้องเข้าใจความตั้งใจ ดังนั้นดูเหมือนว่า Lunaryon จะกระตุ้นให้ผู้อ่านของเขาใช้มาโครเมื่อไม่มีการเพิ่มความหมายใหม่ลงในโปรแกรม แต่เป็นเพียงตัวย่อบางอย่าง สำหรับฉันนี่ฟังดูแปลกและตรงกันข้ามกับการใช้งานมาโครจริง ๆ ด้านล่างเป็นตัวอย่างที่สร้างความหมายใหม่อย่างชัดเจน:

  1. defun และเพื่อนซึ่งเป็นส่วนสำคัญของภาษาไม่สามารถอธิบายได้ในเงื่อนไขเดียวกับที่คุณจะอธิบายถึงการเรียกใช้ฟังก์ชัน

  2. setf(setf (aref x y) z)กฎอธิบายถึงวิธีการฟังก์ชั่นการทำงานมีไม่เพียงพอที่จะอธิบายถึงผลกระทบของการแสดงออกเช่น

  3. with-output-to-stringนอกจากนี้ยังเปลี่ยนความหมายของรหัสภายในแมโคร ในทำนองเดียวกันwith-current-bufferและwith-มาโครอื่น ๆ

  4. dotimes และที่คล้ายกันไม่สามารถอธิบายได้ในแง่ของฟังก์ชั่น

  5. ignore-errors เปลี่ยนซีแมนทิกส์ของโค๊ดที่มันแรป

  6. มีมาโครจำนวนมากที่กำหนดไว้ในcl-libแพคเกจeieioแพ็คเกจและห้องสมุดอื่น ๆ ที่ใช้กันอย่างแพร่หลายใน Emacs Lisp ซึ่งในขณะที่อาจดูเหมือนว่ารูปแบบฟังก์ชั่นมีการตีความที่แตกต่างกัน

ดังนั้นหากมีอะไรมาโครเป็นเครื่องมือในการแนะนำความหมายใหม่ให้เป็นภาษา ฉันไม่สามารถคิดวิธีอื่นในการทำเช่นนั้น (อย่างน้อยก็ไม่ใช่ใน Emacs Lisp)


เมื่อฉันจะไม่ใช้มาโคร:

  1. เมื่อพวกเขาไม่แนะนำโครงสร้างใหม่ใด ๆ ด้วยความหมายใหม่ (เช่นฟังก์ชั่นจะทำงานได้ดีเหมือนกัน)

  2. เมื่อนี่เป็นเพียงความสะดวกสบาย (เช่นการสร้างแมโครที่มีชื่อmvbซึ่งขยายเป็นcl-multiple-value-bindเพียงเพื่อทำให้สั้นลง)

  3. เมื่อฉันคาดว่ารหัสข้อผิดพลาดจำนวนมากจะถูกซ่อนไว้โดยแมโคร (ตามที่ได้รับการบันทึกไว้แมโครจะยากต่อการแก้ไขข้อผิดพลาด)


เมื่อฉันต้องการมากมาโครมากกว่าฟังก์ชั่น:

  1. เมื่อแนวคิดเฉพาะโดเมนถูกบดบังด้วยการเรียกใช้ฟังก์ชัน ตัวอย่างเช่นหากต้องการส่งcontextอาร์กิวเมนต์เดียวกันไปยังฟังก์ชันที่ต้องเรียกใช้อย่างต่อเนื่องฉันต้องการล้อมรอบด้วยแมโครซึ่งcontextอาร์กิวเมนต์นั้นถูกซ่อนไว้

  2. เมื่อรหัสทั่วไปในรูปแบบฟังก์ชั่นเป็น verbose มากเกินไป (ซ้ำซ้ำสร้างใน Emacs Lisp เกินไป verbose กับรสนิยมของฉันและไม่จำเป็นต้องเปิดเผยโปรแกรมเมอร์เพื่อรายละเอียดการใช้งานในระดับต่ำ)


ภาพประกอบ

ด้านล่างนี้เป็นเวอร์ชั่นที่ได้รับการรดน้ำซึ่งมีจุดประสงค์เพื่อให้สัญชาตญาณว่าสิ่งที่มีความหมายโดยความหมายในวิทยาการคอมพิวเตอร์

สมมติว่าคุณมีภาษาการเขียนโปรแกรมอย่างง่าย ๆ ด้วยกฎไวยากรณ์เหล่านี้:

variable := 1 | 0
operation := +
expression := variable | (expression operation expression)

กับภาษานี้เราสามารถสร้าง "โปรแกรม" ชอบ(1+0)+1หรือ0หรือ((0+0))ฯลฯ

แต่ตราบใดที่เรายังไม่มีกฎความหมายโปรแกรม "เหล่านี้ไม่มีความหมาย

สมมติว่าตอนนี้เราติดตั้งภาษาของเราด้วยกฎ (semantic):

0+0  0+1  1+0  1+1  (exp)
---, ---, ---, ---, -----
 0   1+0   1    0    exp

ตอนนี้เราสามารถคำนวณได้โดยใช้ภาษานี้ นั่นคือเราสามารถนำเสนอการสร้างประโยคเข้าใจมันอย่างเป็นนามธรรม (ในคำอื่น ๆ เปลี่ยนเป็นไวยากรณ์ที่เป็นนามธรรม) จากนั้นใช้กฎความหมายเพื่อจัดการกับการเป็นตัวแทนนี้

เมื่อเราพูดถึงตระกูลภาษา Lisp กฎความหมายพื้นฐานของการประเมินฟังก์ชั่นคร่าวๆคือ lambda-แคลคูลัส:

(f x)  (lambda x y)   x
-----, ------------, ---
 fx        ^x.y       x

การอ้างถึงและกลไกการขยายตัวของแมโครนั้นรู้จักกันในชื่อเครื่องมือการเขียนโปรแกรมเมตาคือพวกมันยอมให้ใครคนหนึ่งพูดถึงโปรแกรมแทนที่จะเขียนโปรแกรม พวกเขาประสบความสำเร็จโดยการสร้างความหมายใหม่โดยใช้ "เมตาเลเยอร์" ตัวอย่างของแมโครอย่างง่ายคือ:

(a . b)
-------
(cons a b)

นี่ไม่ใช่แมโครใน Emacs Lisp แต่อาจเป็นได้ ฉันเลือกเพื่อความเรียบง่ายเท่านั้น โปรดสังเกตว่าไม่มีกฎความหมายที่กำหนดไว้ข้างต้นจะใช้กับกรณีนี้เนื่องจากการ(f x)ตีความตัวเลือกที่ใกล้เคียงที่สุดfเป็นฟังก์ชั่นในขณะที่aไม่จำเป็นต้องเป็นฟังก์ชัน


1
"น้ำตาลเชิงกล" ไม่ใช่คำศัพท์ทางวิทยาศาสตร์คอมพิวเตอร์จริงๆ มันเป็นสิ่งที่วิศวกรใช้เพื่อหมายถึงสิ่งต่าง ๆ ดังนั้นฉันไม่มีคำตอบที่ชัดเจน เนื่องจากเรากำลังพูดถึงซีแมนทิกส์ดังนั้นกฎสำหรับการตีความไวยากรณ์ของแบบฟอร์ม: (x y)คือประมาณ "ประเมิน y แล้วจึงเรียก x ด้วยผลลัพธ์ของการประเมินก่อนหน้า" เมื่อคุณเขียน(defun x y)y จะไม่ถูกประเมินและไม่มี x ไม่ว่าคุณจะสามารถเขียนโค้ดที่มีเอฟเฟ็กต์เทียบเท่าโดยใช้ซีแมนทิกส์ที่แตกต่างกันไม่ได้ปฏิเสธโค้ดชิ้นนี้ที่มีซีแมนทิกส์เป็นของตัวเอง
wvxvw

1
พูดถึงความคิดเห็นที่สองของคุณ: คุณช่วยแนะนำฉันถึงคำจำกัดความของแมโครที่คุณใช้ซึ่งบอกว่าคุณอ้างสิทธิ์อะไรได้บ้าง ฉันเพิ่งให้คุณตัวอย่างที่แนะนำกฎความหมายใหม่โดยใช้แมโคร อย่างน้อยก็ในความหมายที่แม่นยำของ CS ฉันไม่คิดว่าการโต้แย้งเกี่ยวกับคำจำกัดความนั้นมีประสิทธิผลและฉันก็คิดว่าคำจำกัดความที่ใช้ใน CS เป็นสิ่งที่ดีที่สุดสำหรับการสนทนานี้
wvxvw

1
ดี ... คุณมีความคิดของคุณเองในสิ่งที่คำว่า "ความหมาย" หมายถึงซึ่งเป็นชนิดของแดกดันถ้าคุณคิดเกี่ยวกับมัน :) แต่จริงๆแล้วสิ่งเหล่านี้มีคำจำกัดความที่แม่นยำมากคุณเพียงแค่ .. ไม่สนใจสิ่งเหล่านั้น หนังสือที่ฉันเชื่อมโยงในคำตอบคือตำราเรียนมาตรฐานเกี่ยวกับความหมายตามที่สอนในหลักสูตร CS ที่สอดคล้องกัน หากคุณเพิ่งอ่านบทความที่เกี่ยวข้องของวิกิพีเดีย: en.wikipedia.org/wiki/Semantics_%28computer_science%29คุณจะเห็นว่ามันเกี่ยวกับอะไร ตัวอย่างคือกฎสำหรับการตีความ(defun x y)แอปพลิเคชัน vs ฟังก์ชัน
wvxvw

โอ้ดีฉันไม่คิดว่านี่เป็นวิธีการอภิปรายของฉัน มันเป็นความผิดพลาดที่จะลองทำใหม่ ฉันลบความคิดเห็นของฉันสำหรับเรื่องนี้ทั้งหมด ฉันขอโทษที่เสียเวลาของคุณ
จันทร์ที่
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.