ภาษาที่ใช้งานได้ทั้งหมดใช้การรวบรวมขยะหรือไม่


32

มีภาษาที่ใช้งานได้ซึ่งอนุญาตให้ใช้ความหมายของสแต็ก - การทำลายที่กำหนดขึ้นโดยอัตโนมัติเมื่อสิ้นสุดขอบเขตหรือไม่?


การทำลายล้างที่กำหนดได้นั้นมีประโยชน์จริง ๆ กับผลข้างเคียง ในบริบทของการเขียนโปรแกรมฟังก์ชั่นที่บริสุทธิ์นั่นหมายถึงการทำให้มั่นใจได้ว่าการกระทำ (monadic) บางอย่างจะทำงานในตอนท้ายของลำดับ นอกเหนือจากนี้แล้วมันง่ายที่จะเขียนภาษาที่มีฟังก์ชั่นเรียงต่อกันซึ่งไม่ต้องการการรวบรวมขยะ
Jon Purdy

ฉันสนใจในคำถามของคำถามมีอะไรเกี่ยวข้องกับ otehr บ้าง?
mattnz

1
ในภาษาที่ใช้งานได้โดยไม่มีการรวบรวมขยะฉันไม่เห็นว่าการแบ่งปันโครงสร้างของโครงสร้างข้อมูลที่ไม่เปลี่ยนรูปได้นั้นเป็นไปได้อย่างไร อาจเป็นไปได้ที่จะสร้างภาษาดังกล่าว แต่ไม่ใช่ภาษาที่ฉันใช้
dan_waterworth

Rust มีคุณสมบัติมากมายที่มักถูกระบุว่าเป็น 'ใช้งานได้' (อย่างน้อยพวกมันมักจะต้องการฟังก์ชั่นที่ใช้งานไม่ได้) ฉันอยากรู้ว่ามันหายไปไหน Immut โดยค่าเริ่มต้น, การปิด, การส่ง func, การโหลดเกินพิกัด, ADT (ยังไม่มี GADT), การจับคู่รูปแบบ, โดยไม่มี GC มีอะไรอีกบ้าง?
Noein

คำตอบ:


10

ไม่ใช่ที่ฉันรู้ แต่ฉันไม่เชี่ยวชาญการเขียนโปรแกรมทำงาน

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

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

สำหรับรหัส monadic ก็มีอีกวิธีหนึ่งซึ่งก็คือ monadic (อันที่จริงหรือเกือบจะเหมือนกัน) และสามารถให้ IORef ที่ถูกทำลายลงโดยอัตโนมัติ หลักการคือการกำหนดการกระทำ "ซ้อน" เมื่อรวมกัน (โดยใช้โอเปอเรเตอร์เชื่อมโยง) สิ่งเหล่านี้จะกำหนดโฟลว์การควบคุมการซ้อน - ฉันคิดว่า "องค์ประกอบ XML" ด้วยค่าซ้ายสุดของการจัดหาคู่เริ่มต้นและแท็กด้านนอก "แท็ก XML" เหล่านี้เป็นเพียงการกำหนดการเรียงลำดับของการกระทำแบบ monadic ที่ระดับที่เป็นนามธรรม

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

การกระทำพิเศษเหล่านี้หลายอย่างอาจมีขั้นตอน "แท็กปิดท้าย" ที่เป็นโมฆะและจะดำเนินการตามขั้นตอน แต่บางคนก็เป็นตัวแทนของการประกาศตัวแปร สิ่งเหล่านี้จะเป็นตัวแทนของตัวสร้างที่มีแท็กเริ่มต้นและ destructor ที่มีแท็กสิ้นสุด คุณได้อะไรเช่น ...

act = terminate ((def-var "hello" ) >>>= \h ->
                 (def-var " world") >>>= \w ->
                 (use-val ((get h) ++ (get w)))
                )

การแปลเป็นองค์ประกอบแบบ monadic ที่มีลำดับการดำเนินการต่อไปนี้แต่ละแท็ก (ไม่ใช่องค์ประกอบ) กลายเป็นการกระทำแบบ monadic ปกติ ...

<def-var val="hello">  --  construction
  <def-var val=" world>  --  construction
    <use-val ...>
      <terminator/>
    </use-val>  --  do nothing
  </def-val>  --  destruction
</def-val>  --  destruction

กฎเช่นนี้อาจทำให้ C ++ - สไตล์ RAII สามารถใช้งานได้ การอ้างอิงที่คล้ายกันของ IORef ไม่สามารถหลบหนีขอบเขตของพวกเขาได้ด้วยเหตุผลที่คล้ายกันว่าทำไม IORef ปกติไม่สามารถหลบหนี Monad ได้ - กฎขององค์ประกอบการเชื่อมโยงนั้นไม่มีทางสำหรับการอ้างอิงเพื่อหลบหนี

แก้ไข - ฉันเกือบลืมบอกว่า - มีพื้นที่แน่นอนฉันไม่แน่ใจเกี่ยวกับที่นี่ สิ่งสำคัญคือต้องแน่ใจว่าตัวแปรภายนอกไม่สามารถอ้างอิงตัวแปรภายในได้ดังนั้นต้องมีข้อ จำกัด หนึ่งสิ่งที่คุณสามารถทำได้กับการอ้างอิงแบบ IORef เหล่านี้ อีกครั้งฉันไม่ได้ทำงานผ่านรายละเอียดทั้งหมด

ดังนั้นการก่อสร้างสามารถเปิดไฟล์ที่ทำลายได้ การก่อสร้างสามารถเปิดซ็อกเก็ตที่ทำลายปิด โดยพื้นฐานแล้วใน C ++ ตัวแปรจะกลายเป็นตัวจัดการทรัพยากร แต่แตกต่างจาก C ++ ไม่มีวัตถุที่จัดสรรฮีปที่ไม่สามารถทำลายได้โดยอัตโนมัติ

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

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


ฉันไม่เห็นสาเหตุที่ GC จำเป็นในทุกภาษาที่ใช้งานได้ ถ้าคุณมีกรอบ RAII ของสไตล์ C ++ คอมไพเลอร์สามารถใช้กลไกนั้นได้เช่นกัน ค่าที่ใช้ร่วมกันจะไม่มีปัญหาสำหรับกรอบงาน RAII (ดูที่ C ++shared_ptr<> ) คุณยังคงถูกทำลายอย่างต่อเนื่อง สิ่งหนึ่งที่เป็นเรื่องยากสำหรับ RAII คือการอ้างอิงแบบวนรอบ RAII ทำงานได้อย่างหมดจดหากกราฟความเป็นเจ้าของเป็นกราฟ Acyclic โดยตรง
MSalters

สิ่งคือสไตล์การเขียนโปรแกรมการทำงานถูกสร้างขึ้นจริงรอบฟังก์ชั่นปิด / lambdas / ไม่ระบุชื่อ หากไม่มี GC คุณจะไม่มีอิสระในการใช้การปิดดังนั้นภาษาของคุณจึงมีการทำงานที่ลดลงอย่างมาก
comingstorm

@comingstorm - C ++ มี lambdas (ตั้งแต่ C ++ 11) แต่ไม่มีตัวรวบรวมขยะมาตรฐาน แลมบ์ดามีสภาพแวดล้อมในการปิดเช่นกัน - และองค์ประกอบในสภาพแวดล้อมนั้นอาจถูกส่งผ่านโดยการอ้างอิงเช่นเดียวกับความเป็นไปได้ของพอยน์เตอร์ที่ถูกส่งผ่านตามค่า แต่ตามที่ฉันเขียนไว้ในย่อหน้าที่สอง C ++ อนุญาตให้มีความเป็นไปได้ของตัวชี้ที่ห้อยอยู่ - เป็นความรับผิดชอบของโปรแกรมเมอร์
Steve314

@MSalters - มีค่าใช้จ่ายที่เกี่ยวข้องในการรับรองว่าจะไม่มีการสร้างรอบอ้างอิง อย่างน้อยก็ไม่ทำให้ภาษามีความรับผิดชอบสำหรับข้อ จำกัด นั้น การกำหนดให้ตัวชี้อาจเป็นการดำเนินการที่ไม่คงที่ แม้ว่ามันอาจจะเป็นตัวเลือกที่ดีที่สุดในบางกรณี การรวบรวมขยะหลีกเลี่ยงปัญหานี้โดยมีค่าใช้จ่ายต่างกัน การทำให้โปรแกรมเมอร์มีความรับผิดชอบเป็นอีกสิ่งหนึ่ง ไม่มีเหตุผลที่ชัดเจนว่าทำไมตัวชี้ห้อยควรเป็นภาษาที่จำเป็น แต่ไม่ใช่ภาษาที่ใช้งานได้ แต่ฉันก็ยังไม่แนะนำให้เขียนตัวชี้ - ห้อย - แฮสเคล
Steve314

ฉันขอยืนยันว่าการจัดการหน่วยความจำด้วยตนเองหมายความว่าคุณไม่มีอิสระในการใช้ C ++ 11 ในการปิด Lisp หรือ Haskell (จริง ๆ แล้วฉันค่อนข้างสนใจในการทำความเข้าใจรายละเอียดของการแลกเปลี่ยนนี้เนื่องจากฉันต้องการเขียนภาษาโปรแกรมการทำงานของระบบ ... )
comingstorm

3

หากคุณคิดว่า C ++ เป็นภาษาที่ใช้งานได้ (มี lambdas) แสดงว่าเป็นตัวอย่างของภาษาที่ไม่ได้ใช้การรวบรวมขยะ


8
ถ้าคุณไม่คิดว่า C ++ เป็นภาษาที่ใช้งานได้ (IMHO มันไม่ใช่แม้ว่าคุณจะสามารถเขียนโปรแกรมที่ใช้งานได้คุณยังสามารถเขียนโปรแกรมที่ไม่มีฟังก์ชั่นที่ใช้งานได้)
mattnz

@mattnz จากนั้นฉันเดาว่าคำตอบจะไม่มีผล ฉันไม่แน่ใจว่าเกิดอะไรขึ้นในภาษาอื่น (เช่นเช่น Haskel)
BЈовиЈ

9
พูดภาษา C ++ สามารถทำงานได้เป็นเหมือนที่บอกว่า Perl เป็นเชิงวัตถุ ...
แบบไดนามิก

คอมไพเลอร์ c ++ อย่างน้อยสามารถตรวจสอบผลข้างเคียงได้ (ผ่านทาง const)
tp1

@ tp1 - (1) ฉันหวังว่านี่จะไม่ถอยหลังไปว่าภาษาของใครดีที่สุดและ (2) นั่นไม่จริงเลย ครั้งแรกผลกระทบที่สำคัญจริงๆส่วนใหญ่เป็น I / O ประการที่สองแม้จะมีเอฟเฟกต์ในหน่วยความจำที่ไม่แน่นอน แต่ const ไม่บล็อกเลย แม้ว่าคุณจะสมมติว่าไม่มีความเป็นไปได้ที่จะล้มล้างระบบย่อย (โดยทั่วไปมีความเหมาะสมใน C ++) แต่ก็มีปัญหาเรื่องความแปรปรวนแบบลอจิคัลและคำหลัก "ไม่แน่นอน" C ++ โดยทั่วไปคุณยังสามารถมีการกลายพันธุ์ได้ คุณคาดว่าจะมั่นใจได้ว่าผลลัพธ์ยังคง "เหมือนเดิม" แต่ไม่จำเป็นต้องมีการนำเสนอซ้ำกัน
Steve314

2

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

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


1
การนับการอ้างอิง IMO คือการรวบรวมขยะและการมีฮีปไม่ได้หมายความว่าเป็นเพียงตัวเลือกเท่านั้น C mallocs และ mfrees ใช้ heap แต่ไม่มีตัวรวบรวมขยะ (มาตรฐาน) และมีการอ้างอิงนับหากคุณเขียนโค้ดเพื่อทำเช่นนั้น C ++ เกือบจะเหมือนกัน - มันมีพอยน์เตอร์อัจฉริยะ (ตามมาตรฐานใน C ++ 11) ที่มีการนับการอ้างอิงในตัว แต่คุณยังสามารถทำคู่มือใหม่และลบได้หากคุณต้องการ
Steve314

สาเหตุทั่วไปของการอ้างว่าการนับการอ้างอิงไม่ใช่การรวบรวมขยะคือมันไม่สามารถรวบรวมรอบการอ้างอิงได้ แน่นอนว่าใช้กับการใช้งานที่ง่าย (อาจรวมถึงตัวชี้สมาร์ท C ++ - ฉันไม่ได้ตรวจสอบ) แต่ก็ไม่ได้เป็นเช่นนั้นเสมอไป อย่างน้อยหนึ่งเครื่องเสมือน Java (โดย IBM, IIRC) ใช้การนับการอ้างอิงเป็นพื้นฐานสำหรับการรวบรวมขยะ
Steve314
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.