Như những người khác đã chỉ ra, Haskell yêu cầu tự động , năng động quản lý bộ nhớ : quản lý bộ nhớ tự động là cần thiết vì quản lý bộ nhớ thủ công là không an toàn; quản lý bộ nhớ động là cần thiết vì đối với một số chương trình, thời gian tồn tại của một đối tượng chỉ có thể được xác định trong thời gian chạy.
Ví dụ, hãy xem xét chương trình sau:
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
Trong chương trình này, danh sách [1..1000]
phải được lưu trong bộ nhớ cho đến khi người dùng gõ "xóa"; vì vậy thời gian tồn tại của điều này phải được xác định động, và đây là lý do tại sao quản lý bộ nhớ động là cần thiết.
Vì vậy, theo nghĩa này, việc phân bổ bộ nhớ động tự động là cần thiết và trong thực tế, điều này có nghĩa là: vâng , Haskell yêu cầu một bộ thu gom rác, vì bộ thu gom rác là trình quản lý bộ nhớ động tự động có hiệu suất cao nhất.
Tuy nhiên...
Mặc dù trình thu gom rác là cần thiết, chúng tôi có thể cố gắng tìm một số trường hợp đặc biệt mà trình biên dịch có thể sử dụng một sơ đồ quản lý bộ nhớ rẻ hơn so với thu gom rác. Ví dụ, đã cho
f :: Integer -> Integer
f x = let x2 = x*x in x2*x2
chúng ta có thể hy vọng trình biên dịch phát hiện x2
có thể được phân bổ một cách an toàn khi f
trả về (thay vì đợi bộ thu gom rác phân bổ x2
). Về cơ bản, chúng tôi yêu cầu trình biên dịch thực hiện phân tích thoát để chuyển đổi các phân bổ trong đống rác được thu gom thành các phân bổ trên ngăn xếp nếu có thể.
Điều này không quá vô lý để yêu cầu: trình biên dịch jhc haskell thực hiện điều này, mặc dù GHC thì không. Simon Marlow nói rằng người thu gom rác thế hệ của GHC khiến cho việc phân tích lối thoát hầu như không cần thiết.
jhc thực sự sử dụng một dạng phân tích thoát phức tạp được gọi là suy luận vùng . Xem xét
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
Trong trường hợp này, một phân tích thoát đơn giản sẽ kết luận rằng x2
thoát khỏi f
(vì nó được trả lại trong bộ tuple), và do đó x2
phải được phân bổ trên đống rác được thu thập. Mặt khác, suy luận vùng có thể phát hiện x2
có thể được phân bổ khi g
trả về; ý tưởng ở đây là x2
nên được phân bổ trong g
khu vực của thay vì f
khu vực của.
Ngoài Haskell
Mặc dù suy luận vùng hữu ích trong một số trường hợp nhất định như đã thảo luận ở trên, nhưng dường như rất khó để dung hòa hiệu quả với đánh giá lười biếng (xem bình luận của Edward Kmett và Simon Peyton Jones ). Ví dụ, hãy xem xét
f :: Integer -> Integer
f n = product [1..n]
Người ta có thể bị cám dỗ để phân bổ danh sách [1..n]
trên ngăn xếp và phân bổ nó sau khi f
trả về, nhưng điều này sẽ thật thảm khốc: nó sẽ thay đổi f
từ việc sử dụng bộ nhớ O (1) (dưới bộ sưu tập rác) thành bộ nhớ O (n).
Công việc mở rộng đã được thực hiện trong những năm 1990 và đầu những năm 2000 về suy luận vùng cho ngôn ngữ chức năng chặt chẽ ML. Mads Tofte, Lars Birkedal, Martin Elsman, Niels Hallenberg đã viết một bài hồi tưởng khá dễ đọc về công việc của họ về suy luận vùng, phần lớn trong số đó họ đã tích hợp vào trình biên dịch MLKit . Họ đã thử nghiệm với quản lý bộ nhớ hoàn toàn dựa trên khu vực (tức là không có bộ thu gom rác) cũng như quản lý bộ nhớ dựa trên khu vực / thu thập rác kết hợp và báo cáo rằng các chương trình thử nghiệm của họ chạy "nhanh hơn từ 10 lần đến 4 lần" so với rác thuần túy- các phiên bản đã sưu tầm.