ทำไมฟังก์ชัน "ไม่ทำอะไร" ของ Haskell, id, ใช้หน่วยความจำมากมาย


112

Haskell มีฟังก์ชัน Identity ซึ่งส่งคืนอินพุตที่ไม่เปลี่ยนแปลง คำจำกัดความนั้นง่ายมาก:

id :: a -> a
id x = x

ดังนั้นเพื่อความสนุกสนานสิ่งนี้ควรส่งออก8:

f = id id id id id id id id id id id id id id id id id id id id id id id id id id id
main = print $ f 8

หลังจากนั้นไม่กี่วินาที (และหน่วยความจำประมาณ 2 GB ตามตัวจัดการงาน) การคอมไพล์ล้มเหลวด้วยghc: out of memory. ghci: out of memoryในทำนองเดียวกันล่ามกล่าวว่า

เนื่องจากidเป็นฟังก์ชันที่ค่อนข้างเรียบง่ายฉันจึงไม่คาดหวังว่ามันจะเป็นภาระหน่วยความจำในเวลาทำงานหรือเวลาคอมไพล์ หน่วยความจำทั้งหมดถูกใช้เพื่ออะไร?


11
คุณต้องการเขียนids เหล่านั้น ใน VIM กับเคอร์เซอร์ในความหมายของการทำเช่นนี้:f :s/id id/id . id ./g
Tobias Brandt

คำตอบ:


135

เราทราบชนิดของid,

id :: a -> a

และเมื่อเรามีความเชี่ยวชาญนี้id idที่ด้านซ้ายสำเนาของidมีประเภท:

id :: (a -> a) -> (a -> a)

และจากนั้นเมื่อคุณมีความเชี่ยวชาญนี้อีกครั้งสำหรับซ้ายสุดidในid id idคุณจะได้รับ:

id :: ((a -> a) -> (a -> a)) -> ((a -> a) -> (a -> a))

คุณจะเห็นแต่ละรายการที่idคุณเพิ่มลายเซ็นของประเภทซ้ายสุดidมีขนาดใหญ่เป็นสองเท่า

โปรดทราบว่าประเภทจะถูกลบระหว่างการคอมไพล์ดังนั้นจะใช้หน่วยความจำใน GHC เท่านั้น จะไม่ใช้หน่วยความจำในโปรแกรมของคุณ


ฉันจำได้ว่า Okasaki ประสบปัญหาคล้าย ๆ กันเมื่อเขาเขียนเครื่องคิดเลข RPN ที่ฝังอยู่ใน Haskell
dfeuer

3
บางทีคำถามก็คือ GHC ควรหาวิธีจัดการเรื่องแบบนี้อย่างสง่างามกว่านี้หรือไม่ โดยเฉพาะอย่างยิ่งประเภทมีขนาดใหญ่มากเมื่อเขียนเต็มรูปแบบ แต่มีการทำซ้ำจำนวนมาก - สามารถใช้การแบ่งปันเพื่อบีบอัดสิ่งเหล่านี้ได้หรือไม่ มีวิธีที่มีประสิทธิภาพในการประมวลผลหรือไม่?
dfeuer

5
@dfeuer ลองขอแบบใน ghci คุณจะเห็นได้จากความเร็วของการตอบสนองว่าจะต้องทำการแบ่งปันที่เหมาะสม ฉันสงสัยว่าการแบ่งปันนี้หายไป - ด้วยเหตุผลที่ชัดเจน - เมื่อคุณแปลเป็นตัวแทนระดับกลางอื่น ๆ (เช่นแกนกลาง)
Daniel Wagner

4
ซึ่งหมายความว่าถ้าidเป็นซ้ำแล้วซ้ำอีกครั้งพื้นที่ของชนิดเป็นสัดส่วนกับn 2^nประเภทที่อนุมานในรหัสของ Ryan จะต้องมี2^27การอ้างอิงถึงตัวแปรประเภทนอกเหนือจากโครงสร้างอื่น ๆ ที่จำเป็นในการแสดงประเภทซึ่งอาจใหญ่กว่าที่คุณคาดว่าจะเป็นประเภทส่วนใหญ่มาก
เดวิด

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