Tại sao hàm "không làm gì" của Haskell, id, lại tiêu tốn rất nhiều bộ nhớ?


112

Haskell có một chức năng nhận dạng trả về đầu vào không thay đổi. Định nghĩa rất đơn giản:

id :: a -> a
id x = x

Vì vậy, để giải trí, điều này sẽ xuất ra 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

Sau một vài giây (và khoảng 2 gb bộ nhớ theo Trình quản lý tác vụ), quá trình biên dịch không thành công với ghc: out of memory. Tương tự, thông dịch viên nói ghci: out of memory.

idlà một chức năng khá đơn giản, tôi sẽ không mong đợi nó trở thành gánh nặng về bộ nhớ trong thời gian chạy hoặc thời gian biên dịch. Tất cả bộ nhớ đang được sử dụng để làm gì?


11
Bạn muốn soạn những cái idđó. Trong VIM, với con trỏ vào định nghĩa của f, làm như sau: :s/id id/id . id ./g.
Tobias Brandt

Câu trả lời:


135

Chúng tôi biết loại id,

id :: a -> a

Và khi chúng tôi chuyên về điều này id id, bản sao bên trái của idcó loại:

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

Và sau đó khi bạn chuyên này một lần nữa cho tận cùng bên trái idtrong id id id, bạn nhận được:

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

Vì vậy, bạn thấy mỗi idbạn thêm, chữ ký loại ở ngoài cùng bên trái idlớn gấp đôi.

Lưu ý rằng các kiểu sẽ bị xóa trong quá trình biên dịch, vì vậy điều này sẽ chỉ chiếm bộ nhớ trong GHC. Nó sẽ không chiếm bộ nhớ trong chương trình của bạn.


Tôi nhớ Okasaki đã gặp một số rắc rối tương tự khi anh ấy viết một máy tính RPN được nhúng vào Haskell.
dfeuer

3
Có lẽ câu hỏi đặt ra là GHC có nên tìm cách xử lý loại việc này một cách nhẹ nhàng hơn hay không. Đặc biệt, loại này rất lớn khi được viết ra toàn bộ, nhưng có rất nhiều sự trùng lặp — liệu chia sẻ có thể được sử dụng để nén những thứ như vậy không? Có cách nào hiệu quả để xử lý chúng không?
dfeuer

5
@dfeuer Thử hỏi loại ghci. Bạn sẽ thấy bằng tốc độ phản hồi rằng nó phải đang thực hiện chia sẻ thích hợp. Tôi nghi ngờ rằng chia sẻ này bị mất - vì những lý do rõ ràng - khi bạn dịch sang một số đại diện trung gian khác (ví dụ: cốt lõi).
Daniel Wagner

4
Điều này có nghĩa là nếu idđược lặp lại nhiều nlần, không gian của kiểu của nó tỷ lệ với 2^n. Kiểu được suy ra trong mã của Ryan sẽ cần 2^27tham chiếu đến biến kiểu của nó ngoài cấu trúc khác cần thiết để đại diện cho kiểu, có thể lớn hơn rất nhiều so với những gì bạn mong đợi ở hầu hết các kiểu.
David

58
Thực hiện suy luận kiểu một cách ngây thơ là nhân đôi số mũ, bằng cách khéo léo sử dụng chia sẻ trong các biểu thức kiểu, bạn có thể đưa nó xuống chỉ theo cấp số nhân. Nhưng không quan trọng bạn làm gì, sẽ có một số biểu hiện khá đơn giản khiến trình kiểm tra kiểu phát nổ. May mắn thay, những điều này không xảy ra trong lập trình thực tế.
augustss
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.