Haskell ต้องการคนเก็บขยะหรือไม่?


119

ฉันสงสัยว่าทำไมการใช้งาน Haskell จึงใช้ GC

ฉันไม่สามารถนึกถึงกรณีที่ GC จำเป็นในภาษาที่บริสุทธิ์ เป็นเพียงการเพิ่มประสิทธิภาพเพื่อลดการคัดลอกหรือจำเป็นจริง ๆ ?

ฉันกำลังมองหาโค้ดตัวอย่างที่อาจรั่วไหลหากไม่มี GC


15
คุณอาจพบว่าซีรีส์นี้ให้ความกระจ่าง ครอบคลุมถึงวิธีการสร้างขยะ (และรวบรวมในภายหลัง): blog.ezyang.com/2011/04/the-haskell-heap
Tom Crockett

6
มีการอ้างอิงทุกที่ในภาษาบริสุทธิ์! ก็ไม่แน่นอนอ้างอิง
Tom Crockett

1
@pelotom การอ้างอิงถึงข้อมูลที่ไม่เปลี่ยนรูปหรือการอ้างอิงที่ไม่เปลี่ยนรูป?
Pubby

3
ทั้งสอง ความจริงที่ว่าข้อมูลที่อ้างถึงนั้นไม่เปลี่ยนรูปตามมาจากข้อเท็จจริงที่ว่าการอ้างอิงทั้งหมดไม่เปลี่ยนรูปลงไปทั้งหมด
Tom Crockett

4
คุณจะได้รับความสนใจอย่างแน่นอนโดยลังเลปัญหาเช่นการใช้เหตุผลนี้ในการจัดสรรหน่วยความจำจะช่วยให้เข้าใจว่าทำไม deallocation ไม่สามารถคาดการณ์แบบคงที่ในกรณีทั่วไป อย่างไรก็ตามมีบางโปรแกรมที่สามารถคาดเดาการยกเลิกการจัดสรรได้เช่นเดียวกับโปรแกรมบางโปรแกรมที่ทราบว่าจะยุติโดยไม่ต้องเรียกใช้งานจริง
Paul R

คำตอบ:


218

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

ตัวอย่างเช่นพิจารณาโปรแกรมต่อไปนี้:

main = loop (Just [1..1000]) where
  loop :: Maybe [Int] -> IO ()
  loop obj = do
    print obj
    resp <- getLine
    if resp == "clear"
     then loop Nothing
     else loop obj

ในโปรแกรมนี้รายการ[1..1000]จะต้องถูกเก็บไว้ในหน่วยความจำจนกว่าผู้ใช้จะพิมพ์ "clear"; ดังนั้นจึงต้องกำหนดอายุการใช้งานแบบไดนามิกและนี่คือเหตุผลที่จำเป็นต้องมีการจัดการหน่วยความจำแบบไดนามิก

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

อย่างไรก็ตาม ...

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

f :: Integer -> Integer
f x = let x2 = x*x in x2*x2

เราอาจหวังให้คอมไพลเลอร์ตรวจพบว่าx2สามารถยกเลิกการจัดสรรได้อย่างปลอดภัยเมื่อfส่งคืน (แทนที่จะรอให้ตัวรวบรวมขยะยกเลิกการจัดสรรx2) โดยพื้นฐานแล้วเรากำลังขอให้คอมไพเลอร์ทำการวิเคราะห์การหลบหนีเพื่อแปลงการปันส่วนในฮีปที่รวบรวมขยะเป็นการจัดสรรบนสแต็กทุกที่ที่ทำได้

นี่ไม่ใช่เรื่องเกินสมควรที่จะถาม: คอมไพเลอร์ jhc haskellทำสิ่งนี้แม้ว่า GHC จะไม่ทำก็ตาม Simon Marlow กล่าวว่าคนเก็บขยะในยุคของ GHC ทำให้การวิเคราะห์การหลบหนีไม่จำเป็นเป็นส่วนใหญ่

JHC จริงใช้เป็นรูปแบบที่มีความซับซ้อนของการวิเคราะห์การหลบหนีที่รู้จักกันในภูมิภาคอนุมาน พิจารณา

f :: Integer -> (Integer, Integer)
f x = let x2 = x * x in (x2, x2+1)

g :: Integer -> Integer
g x = case f x of (y, z) -> y + z

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

นอกเหนือจาก Haskell

แม้ว่าการอนุมานภูมิภาคจะมีประโยชน์ในบางกรณีตามที่กล่าวไว้ข้างต้น แต่ดูเหมือนว่าจะเป็นการยากที่จะกระทบยอดอย่างมีประสิทธิภาพด้วยการประเมินแบบขี้เกียจ (ดูความคิดเห็นของ Edward KmettและSimon Peyton Jones ) ตัวอย่างเช่นพิจารณา

f :: Integer -> Integer
f n = product [1..n]

อาจถูกล่อลวงให้จัดสรรรายการ[1..n]บนสแต็กและยกเลิกการจัดสรรหลังจากfส่งคืน แต่สิ่งนี้จะเป็นหายนะ: จะเปลี่ยนfจากการใช้หน่วยความจำ O (1) (ภายใต้การรวบรวมขยะ) เป็นหน่วยความจำ O (n)

งานที่กว้างขวางเกิดขึ้นในปี 1990 และต้นปี 2000 ในการอนุมานภูมิภาคสำหรับML ภาษาที่ใช้งานได้อย่างเข้มงวด Mads ทอฟเตลาร์ส Birkedal มาร์ติน Elsman นีลส์ Hallenberg ได้เขียนอ่านได้ค่อนข้างย้อนหลังในการทำงานของพวกเขาในภูมิภาคอนุมานมากที่พวกเขารวมอยู่ในคอมไพเลอร์ MLKit พวกเขาทดลองกับการจัดการหน่วยความจำตามภูมิภาคอย่างหมดจด (เช่นไม่มีตัวเก็บขยะ) รวมถึงการจัดการหน่วยความจำแบบไฮบริดตามภูมิภาค / ที่เก็บรวบรวมขยะและรายงานว่าโปรแกรมทดสอบของพวกเขาทำงาน "เร็วกว่า 10 เท่าและช้ากว่า 4 เท่า" มากกว่าขยะบริสุทธิ์ รุ่นที่รวบรวม


2
Haskell ต้องการการแบ่งปันหรือไม่? หากไม่เป็นเช่นนั้นในตัวอย่างแรกของคุณคุณสามารถส่งสำเนารายการ (resp. Nothing) ไปยังการเรียกซ้ำloopและยกเลิกการจัดสรรรายการเก่า - ไม่ทราบอายุการใช้งาน แน่นอนว่าไม่มีใครต้องการการใช้งาน Haskell แบบไม่แชร์เพราะมันช้ามากสำหรับโครงสร้างข้อมูลขนาดใหญ่
มี่

3
ฉันชอบคำตอบนี้มากแม้ว่าความสับสนเพียงอย่างเดียวของฉันคือตัวอย่างแรก เห็นได้ชัดว่าหากผู้ใช้ไม่เคยพิมพ์คำว่า "ชัดเจน" ก็จะสามารถใช้หน่วยความจำที่ไม่มีที่สิ้นสุด (โดยไม่มี GC) แต่นั่นไม่ใช่การรั่วไหลเนื่องจากหน่วยความจำยังคงถูกติดตาม
Pubby

3
C ++ 11 มีการใช้งานตัวชี้อัจฉริยะที่ยอดเยี่ยม โดยทั่วไปจะใช้การนับอ้างอิง ฉันเดาว่า Haskell สามารถทิ้งการเก็บขยะเพื่อประโยชน์ของสิ่งที่คล้ายกันดังนั้นจึงกลายเป็นปัจจัยกำหนด
intrepidis

3
@ChrisNash - ไม่ได้ผล พอยน์เตอร์อัจฉริยะใช้การนับอ้างอิงใต้ฝากระโปรง การนับอ้างอิงไม่สามารถจัดการกับโครงสร้างข้อมูลที่มีรอบ Haskell สามารถสร้างโครงสร้างข้อมูลด้วยวัฏจักร
Stephen C

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

27

ลองมาเป็นตัวอย่างเล็กน้อย ระบุสิ่งนี้

f (x, y)

คุณต้องการที่จะจัดสรรทั้งคู่อยู่ที่ไหนสักแห่งก่อนที่จะเรียก(x, y) fคุณสามารถยกเลิกการจัดสรรคู่นั้นได้เมื่อใด คุณไม่มีความคิด ไม่สามารถยกเลิกการจัดสรรได้เมื่อfส่งคืนเนื่องจากfอาจจะใส่คู่ในโครงสร้างข้อมูล (เช่นf p = [p]) fดังนั้นชีวิตของทั้งคู่อาจจะต้องมีความยาวเกินกว่าที่กลับมาจาก ตอนนี้บอกว่าทั้งคู่ถูกจัดให้อยู่ในรายชื่อใครจะสามารถแยกรายการออกจากกันได้หรือไม่? ไม่เพราะอาจมีการแชร์ทั้งคู่ (เช่นlet p = (x, y) in (f p, p)) ดังนั้นจึงยากที่จะบอกได้ว่าเมื่อใดที่สามารถยกเลิกการจัดสรรทั้งคู่ได้

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

ดังนั้นฉันต้องการเปลี่ยนคำถาม ทำไมคุณถึงคิดว่า Haskell ไม่ต้องการ GC คุณจะแนะนำการจัดสรรหน่วยความจำอย่างไร?


18

สัญชาตญาณของคุณที่ว่าสิ่งนี้เกี่ยวข้องกับความบริสุทธิ์มีความจริงบางอย่าง

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

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

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

ในความเป็นจริงCleanซึ่งเป็นญาติของ Haskell มีประเภทเชิงเส้น (อย่างเคร่งครัดมากขึ้น: ไม่ซ้ำกัน) และสามารถให้ความคิดได้ว่าการไม่อนุญาตให้คัดลอกเป็นอย่างไร แต่ Clean ยังอนุญาตให้คัดลอกสำหรับประเภท "ไม่ซ้ำกัน" ได้

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

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

นอกจากนี้ยังน่าสนใจที่จะเปรียบเทียบกับ Forth (หรือภาษาที่ใช้สแต็กอื่น ๆ ) ซึ่งมีการดำเนินการ DUP อย่างชัดเจนซึ่งทำให้เกิดความชัดเจนเมื่อเกิดการทำซ้ำ

อื่น ๆ (นามธรรมมากขึ้น) วิธีคิดเกี่ยวกับเรื่องนี้คือการทราบว่า Haskell diag :: X -> (X, X)ถูกสร้างขึ้นจากเพียงแค่พิมพ์แลมบ์ดาแคลคูลัสซึ่งอยู่บนพื้นฐานทฤษฎีของประเภทปิดคาร์ทีเซียนและที่ประเภทเช่นมาพร้อมกับฟังก์ชั่นในแนวทแยง ภาษาที่อิงตามคลาสของหมวดหมู่อื่นอาจไม่มีสิ่งนั้น

แต่โดยทั่วไปการเขียนโปรแกรมเชิงเส้นอย่างหมดจดนั้นยากเกินกว่าที่จะเป็นประโยชน์ดังนั้นเราจึงตัดสินใจเลือก GC


3
เนื่องจากฉันเขียนคำตอบนี้ภาษาการเขียนโปรแกรม Rust จึงได้รับความนิยมเพิ่มขึ้นไม่น้อย ดังนั้นจึงควรค่าแก่การกล่าวถึงว่า Rust ใช้ระบบประเภท linear-ish เพื่อควบคุมการเข้าถึงหน่วยความจำและควรดูหากคุณต้องการดูแนวคิดที่ฉันกล่าวถึงในทางปฏิบัติ
sigfpe

14

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

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


2
"พวกเขาไม่เคยกลายพันธุ์ค่าก่อนหน้า": คุณสามารถตรวจสอบhaskell.org/haskellwiki/HaskellImplementorsWorkshop/2011/Takanoซึ่งเกี่ยวกับส่วนขยาย GHC ทดลองที่นำหน่วยความจำกลับมาใช้ใหม่
gfour

11

หากภาษา (ภาษาใดก็ได้) อนุญาตให้คุณจัดสรรวัตถุแบบไดนามิกมีสามวิธีที่ใช้ได้จริงในการจัดการกับการจัดการหน่วยความจำ:

  1. ภาษาสามารถอนุญาตให้คุณจัดสรรหน่วยความจำบนสแตกหรือเมื่อเริ่มต้นเท่านั้น แต่ข้อ จำกัด เหล่านี้ จำกัด ประเภทของการคำนวณที่โปรแกรมสามารถทำได้อย่างรุนแรง (ในทางปฏิบัติในทางทฤษฎีคุณสามารถเลียนแบบโครงสร้างข้อมูลแบบไดนามิกใน (พูด) Fortran โดยการแทนค่าในอาร์เรย์ขนาดใหญ่มันน่ากลัว ... และไม่เกี่ยวข้องกับการสนทนานี้)

  2. ภาษาสามารถให้ชัดเจนfreeหรือdisposeกลไก แต่นี่ต้องอาศัยโปรแกรมเมอร์เพื่อทำให้ถูกต้อง ความผิดพลาดใด ๆ ในการจัดการพื้นที่เก็บข้อมูลอาจทำให้หน่วยความจำรั่ว ... หรือแย่กว่านั้น

  3. ภาษา (หรือมากกว่านั้นคือการนำภาษาไปใช้อย่างเคร่งครัด) สามารถจัดเตรียมตัวจัดการหน่วยเก็บข้อมูลอัตโนมัติสำหรับหน่วยเก็บข้อมูลที่จัดสรรแบบไดนามิก เช่นคนเก็บขยะบางรูปแบบ

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

การนำสิ่งนี้ไปใช้กับ Haskell ภาษาไม่มีข้อ จำกัด คือ 1. และไม่มีการดำเนินการยกเลิกการจัดสรรด้วยตนเองตามข้อ 2 ดังนั้นเพื่อให้สามารถใช้งานได้กับสิ่งที่ไม่สำคัญการใช้งาน Haskell จำเป็นต้องรวมตัวเก็บขยะ .

ฉันไม่สามารถนึกถึงกรณีที่ GC จำเป็นในภาษาที่บริสุทธิ์

สันนิษฐานว่าคุณหมายถึงภาษาที่ใช้งานได้จริง

คำตอบคือต้องใช้ GC ภายใต้ประทุนเพื่อเรียกคืนฮีปอ็อบเจ็กต์ที่ภาษาต้องสร้างขึ้น ตัวอย่างเช่น.

  • ฟังก์ชันบริสุทธิ์จำเป็นต้องสร้างวัตถุฮีปเนื่องจากในบางกรณีจะต้องส่งคืนวัตถุเหล่านั้น นั่นหมายความว่าไม่สามารถจัดสรรบนสแตกได้

  • ความจริงที่ว่าอาจมีรอบได้ (เป็นผลมาจากlet recตัวอย่าง) หมายความว่าวิธีการนับอ้างอิงจะใช้ไม่ได้กับฮีปอ็อบเจ็กต์

  • จากนั้นจะมีการปิดฟังก์ชัน ... ซึ่งไม่สามารถจัดสรรบนสแต็กได้เนื่องจากมีอายุการใช้งานที่ (โดยทั่วไป) เป็นอิสระจากสแต็กเฟรมที่สร้างขึ้น

ฉันกำลังมองหาโค้ดตัวอย่างที่อาจรั่วไหลหากไม่มี GC

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


2
ทำไมคุณถึงคิดว่ารายการตัวเลือกของคุณมีข้อมูลครบถ้วน ARC ใน Objective C การอนุมานภูมิภาคใน MLKit และ DDC รวบรวมการรวบรวมขยะตามเวลาใน Mercury - ทั้งหมดนี้ไม่เข้ากับรายการนี้
ดีจันทร์ที่

@DeeMon - พวกเขาทั้งหมดเข้ากันได้กับหนึ่งในประเภทเหล่านั้น หากคุณคิดว่าไม่เป็นเพราะคุณวาดขอบเขตหมวดหมู่แน่นเกินไป เมื่อฉันพูดว่า "การเก็บขยะบางรูปแบบ" ฉันหมายถึงกลไกใด ๆที่พื้นที่เก็บข้อมูลจะถูกเรียกคืนโดยอัตโนมัติ
Stephen C

1
C ++ 11 ใช้ตัวชี้อัจฉริยะ โดยทั่วไปจะใช้การนับอ้างอิง เป็นไปตามกำหนดและเป็นไปโดยอัตโนมัติ ฉันชอบที่จะเห็นการใช้งาน Haskell ใช้วิธีนี้
intrepidis

2
@ChrisNash - 1) มันใช้ไม่ได้ การอ้างอิงฐานการนับฐานข้อมูลรั่วไหลหากมีรอบ ... เว้นแต่คุณจะสามารถพึ่งพารหัสแอปพลิเคชันเพื่อทำลายวงจรได้ 2) เป็นที่ทราบกันดี (สำหรับผู้ที่ศึกษาสิ่งเหล่านี้) ว่าการนับอ้างอิงทำงานได้ไม่ดีเมื่อเทียบกับเครื่องเก็บขยะสมัยใหม่ (ของจริง)
Stephen C

@DeeMon - นอกจากนี้โปรดดูคำตอบของ Reinerp เกี่ยวกับสาเหตุที่การอนุมานภูมิภาคไม่สามารถใช้ได้จริงกับ Haskell
Stephen C

8

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


นี่เป็นคำตอบว่าทำไม GC จึงไม่จำเป็นโดยทั่วไป แต่ฉันสนใจ Haskell เป็นพิเศษมากกว่า
Pubby

10
หาก GC ไม่จำเป็นในทางทฤษฎีโดยทั่วไปแล้วมันก็เป็นเรื่องเล็กน้อยตามมาว่าในทางทฤษฎีไม่จำเป็นสำหรับ Haskell เช่นกัน
เอ๊ะ

@ ประการที่สามฉันตั้งใจจะบอกว่าจำเป็นฉันคิดว่าเครื่องตรวจการสะกดของฉันพลิกความหมาย
Pubby

1
ความคิดเห็นของ Ehird ยังคงมี :-)
Paul R

2

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

อย่างไรก็ตามหากคุณไม่เชื่อฉันเพียงแค่ลองใช้ภาษา FP แล้วคุณจะเห็นว่าฉันพูดถูก

แก้ไข: ฉันลืม ความเกียจคร้านเป็นนรกที่ไม่มี GC ไม่เชื่อฉัน? เพียงแค่ลองโดยไม่ต้องใช้ GC ในตัวอย่างเช่น C ++ คุณจะเห็น ... สิ่งต่างๆ


1

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

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


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