เหตุใดการเขียนคอมไพเลอร์ในภาษาที่ใช้งานได้จึงง่ายกว่า [ปิด]


89

ฉันคิดถึงคำถามนี้มานานมาก แต่ก็ไม่พบคำตอบใน Google และคำถามที่คล้ายกันใน Stackoverflow หากมีการซ้ำกันขออภัยด้วย

หลายคนดูเหมือนจะพูดว่าการเขียนคอมไพเลอร์และเครื่องมือภาษาอื่น ๆ ในภาษาที่ใช้งานได้เช่น OCaml และ Haskell นั้นมีประสิทธิภาพมากกว่าและง่ายกว่ามากจากนั้นจึงเขียนเป็นภาษาที่จำเป็น

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


5
ฉันจะไม่บอกว่ามันง่ายกว่า แต่ลักษณะการทำงานของการรวบรวมงานเช่นการแยกวิเคราะห์อาจเป็นไปตามธรรมชาติในการเขียนโปรแกรมเชิงฟังก์ชัน ภาษาที่ใช้งานได้เช่น OCaml สามารถทำได้เร็วมากโดยเทียบกับความเร็วของ C.
Robert Harvey

17
คนนี้เป็นเรื่องโต้แย้งจริงหรือ? แน่นอนว่ามีใครบางคนมีความเข้าใจ ฉันอยากจะรู้จักตัวเอง
Robert Harvey

ฉันคิดว่าอย่างน้อยก็ควรมีเหตุผลที่ดีว่าทำไมต้องใช้ภาษาที่ใช้งานได้มากกว่าภาษาที่จำเป็น ฉันพบบทความบางส่วนซึ่งโดยพื้นฐานแล้วภาษาที่ใช้งานได้นั้นไม่มีผลข้างเคียงและเช่นนั้น แต่มันก็ไม่ชัดเจนเลย อย่างไรก็ตามหากนี่เป็นข้อโต้แย้งก็ควรปิดคำถามหรือปรับคำถามใหม่
wvd

12
เป็นเรื่องโต้แย้งจริงหรือไม่ที่จะยอมรับว่าบางสิ่งบางอย่างเหมาะกับรูปแบบของภาษานั้น ๆ ? "เหตุใด C จึงดีกว่า Javascript ในการเขียนไดรเวอร์อุปกรณ์" ฉันคิดว่า ...
CA McCann

1
ฉันคิดว่ามันจะตรงกันข้าม ฉันกำลังอ่าน "คอมไพเลอร์จิ๋วสุด ๆ " และใช้การกลายพันธุ์ของตัวแปรในทุกที่
Rolf

คำตอบ:


102

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

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


คำตอบที่สมบูรณ์ที่สุดจนถึงตอนนี้ฉันจะทำเครื่องหมายว่านี่เป็นคำตอบที่ยอมรับได้ แต่ฉันคิดว่าคำตอบของ Pete Kirkham ก็ดีเช่นกัน
wvd

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

3
@WarrenP: แนวคิด "รหัสพกพา" มาจากภาษาที่ใช้งานได้แบบคงที่ แนวคิดก็คือคุณใช้ type-system ในลักษณะนี้เพื่อให้ฟังก์ชันสามารถตรวจสอบได้ว่าถูกต้องหรือไม่ดังนั้นความจริงที่ว่า code compiles คือข้อพิสูจน์ความถูกต้อง แน่นอนว่านี่เป็นไปไม่ได้อย่างเต็มที่ในขณะที่การรักษาภาษาให้สมบูรณ์และการตรวจสอบตัวพิมพ์สามารถตัดสินใจได้ แต่ยิ่งระบบประเภทแข็งแกร่งคุณก็จะไปถึงเป้าหมายนั้นได้มากขึ้นเท่านั้น
sepp2k

3
เหตุผลที่แนวคิดนี้ส่วนใหญ่ได้รับความนิยมในชุมชนการทำงานคือในภาษาที่มีสถานะไม่แน่นอนคุณจะต้องเข้ารหัสข้อมูลเกี่ยวกับเวลาและสถานที่ที่การเปลี่ยนแปลงสถานะเกิดขึ้นในประเภทต่างๆ ในภาษาที่คุณรู้ว่าผลลัพธ์ของฟังก์ชันขึ้นอยู่กับอาร์กิวเมนต์เท่านั้นการเข้ารหัสการพิสูจน์ในประเภทนั้นง่ายกว่ามาก (นอกจากนี้ยังง่ายกว่ามากในการพิสูจน์ความถูกต้องของรหัสด้วยตนเองเนื่องจากคุณไม่ต้องพิจารณาว่าสถานะโลกใด เป็นไปได้และจะส่งผลต่อพฤติกรรมของฟังก์ชันอย่างไร) อย่างไรก็ตามสิ่งนี้ไม่เกี่ยวข้องกับคอมไพเลอร์โดยเฉพาะ
sepp2k

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

38

งานคอมไพเลอร์จำนวนมากคือการจับคู่รูปแบบกับโครงสร้างแบบต้นไม้

ทั้ง OCaml และ Haskell มีความสามารถในการจับคู่รูปแบบที่มีประสิทธิภาพและรัดกุม

การเพิ่มการจับคู่รูปแบบกับภาษาที่จำเป็นจะทำได้ยากกว่าเนื่องจากค่าใดก็ตามที่ได้รับการประเมินหรือแยกออกเพื่อให้ตรงกับรูปแบบจะต้องปราศจากผลข้างเคียง


ฟังดูเหมือนเป็นคำตอบที่สมเหตุสมผล แต่นี่เป็นเพียงสิ่งเดียวหรือไม่? เช่นสิ่งต่างๆเช่นการวนซ้ำหางจะมีบทบาทด้วยหรือไม่?
wvd

ดูเหมือนจะบ่งบอกว่าเป็นปัญหาของระบบ type มากกว่าโมเดลการดำเนินการจริง บางสิ่งที่ขึ้นอยู่กับการเขียนโปรแกรมที่จำเป็นซึ่งมีค่าที่ไม่เปลี่ยนรูปแทนประเภทโครงสร้างอาจใช้ได้ดี
Donal Fellows

@wvd: การเพิ่มประสิทธิภาพการเรียกซ้ำหางเป็นรายละเอียดการใช้งานไม่ใช่คุณลักษณะภาษาเช่นนี้ซึ่งทำให้ฟังก์ชันการเรียกซ้ำเชิงเส้นเทียบเท่ากับการวนซ้ำแบบวนซ้ำ ฟังก์ชันเรียกซ้ำเพื่อเดินรายการที่เชื่อมโยงใน C จะได้รับประโยชน์จากมันเช่นเดียวกับการเรียกซ้ำในรายการใน Scheme
CA McCann

@wvd gcc C มีการกำจัดการเรียกหางเช่นเดียวกับภาษาของรัฐที่ไม่แน่นอนอื่น ๆ
Pete Kirkham

3
@camccann: หากมาตรฐานภาษารับประกัน tco (หรืออย่างน้อยก็รับประกันว่าฟังก์ชันการเรียกซ้ำของรูปแบบหนึ่ง ๆ จะไม่ทำให้เกิดสแต็กล้นหรือการใช้หน่วยความจำเพิ่มขึ้นเชิงเส้น) ฉันจะพิจารณาคุณสมบัติภาษานั้น หากมาตรฐานไม่รับประกัน แต่คอมไพลเลอร์ก็ทำเช่นนั้นก็เป็นคุณสมบัติของคอมไพเลอร์
sepp2k

15

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

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

ที่กล่าวว่าภาษาที่ใช้งานได้เป็นเครื่องมือที่ดีที่สุดในการแก้ปัญหาใด ๆ ดังนั้นจึงมีเหตุผลตามมาว่าเป็นเครื่องมือที่ดีที่สุดในการแก้ปัญหานี้โดยเฉพาะ QED

/ me: คลานกลับไปที่งาน Java ของฉันอย่างสนุกสนานเล็กน้อย ...


4
-1 สำหรับ "ภาษาที่ใช้งานได้เป็นเครื่องมือที่ดีที่สุดในการแก้ปัญหา" หากเป็นจริงเราทุกคนจะใช้มันทุกที่ ;)
Andrei Krotkov

15
@ Andrei Krotkov: คำพูดของวันนี้คือ fa · ce ·การออกเสียงที่น่าเบื่อ: \ fə-ˈsē-shəs \ Function: คำคุณศัพท์นิรุกติศาสตร์: แง่มุมภาษาฝรั่งเศสตอนกลางจาก facetie jest จากภาษาละตินวันที่: 1599 1: การล้อเล่นหรือการพูดติดตลกมักไม่เหมาะสม : waggish <just being facetious> 2: หมายถึงการมีอารมณ์ขันหรือขบขัน: ไม่จริงจัง <a แง่คิดแง่มุม> คำพ้องความหมายดูมีไหวพริบนอกเหนือจากเรื่องตลกที่หายไปตรรกะของคุณยังมีข้อบกพร่อง คุณกำลังสมมติว่าทุกคนเป็นนักแสดงที่มีเหตุผลและที่ฉันกลัวไม่ใช่ข้อสันนิษฐานที่ยุติธรรม
Ukko

5
ฉันเดาว่าฉันพลาดเรื่องตลกเพราะฉันรู้จักผู้คนในชีวิตจริงที่มักจะพูดในสิ่งที่แน่นอนยกเว้นอย่างจริงจัง กฎหมายของโปฉันเดา tvtropes.org/pmwiki/pmwiki.php/Main/PoesLaw
Andrei Krotkov

13
@ Andrei: การใช้อาร์กิวเมนต์โฆษณาป๊อปลัม: "ถ้าเหตุผลดีกว่าความไม่รู้ทางอารมณ์เราทุกคนจะใช้มันทุกที่"
Tim Schaeffer

9

โดยพื้นฐานแล้วคอมไพเลอร์คือการแปลงจากรหัสชุดหนึ่งไปยังอีกชุดหนึ่ง - จากแหล่งที่มาเป็น IR จาก IR เป็น IR ที่ปรับให้เหมาะสมจาก IR ไปจนถึงการประกอบ ฯลฯ นี่คือประเภทของภาษาที่ใช้งานได้อย่างแม่นยำได้รับการออกแบบมาเพื่อ - ฟังก์ชันที่บริสุทธิ์คือ เพียงแค่การเปลี่ยนแปลงจากสิ่งหนึ่งไปเป็นอีกสิ่งหนึ่ง ฟังก์ชันที่จำเป็นไม่มีคุณสมบัตินี้ แม้ว่าคุณจะสามารถเขียนโค้ดประเภทนี้ในภาษาที่จำเป็นได้ แต่ภาษาที่ใช้งานได้นั้นมีความเชี่ยวชาญเป็นพิเศษ


6

ดูสิ่งนี้ด้วย

รูปแบบการออกแบบ F #

FP จัดกลุ่มสิ่งต่างๆ 'ตามการดำเนินการ' ในขณะที่ OO จัดกลุ่มสิ่งต่างๆ 'ตามประเภท' และ 'โดยการดำเนินการ' นั้นเป็นธรรมชาติมากกว่าสำหรับคอมไพเลอร์ / ล่าม


3
สิ่งนี้เกี่ยวข้องกับสิ่งที่เรียกว่าในแวดวงทฤษฎีภาษาการเขียนโปรแกรมบางส่วน "ปัญหาการแสดงออก" ตัวอย่างเช่นดูคำถามนี้ซึ่งฉันจะแสดงรหัส Haskell ที่น่ากลัวอย่างแท้จริงซึ่งทำสิ่งต่างๆในลักษณะ "ขยายได้" ในทางตรงกันข้ามการบังคับให้ภาษา OOP เป็นรูปแบบ "การดำเนินการที่ขยายได้" มีแนวโน้มที่จะกระตุ้นรูปแบบผู้เข้าชม
CA McCann

6

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


4

ดูเหมือนทุกคนจะพลาดเหตุผลสำคัญอีกประการหนึ่ง การเขียนภาษาเฉพาะโดเมนแบบฝัง (EDSL) เป็นเรื่องง่ายมากสำหรับตัวแยกวิเคราะห์ซึ่งมีลักษณะคล้ายกับ (E) BNF ในโค้ดปกติ ตัวแยกวิเคราะห์เช่นพาร์เซกนั้นค่อนข้างง่ายในการเขียนในภาษาที่ใช้งานได้โดยใช้ฟังก์ชันลำดับที่สูงกว่าและองค์ประกอบของฟังก์ชัน ไม่เพียง แต่ง่ายกว่า แต่สวยงามมาก

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

นี่ไม่ใช่วิธีเดียวในการสร้างกรอบความคิดที่แน่นอน

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