5

If I define the Fibonacci sequence recursively:

fibo_lazy_list = 0 : 1 : zipWith (+) fibo_lazy_list (tail fibo_lazy_list)

Then ask for the first element above a given value, say:

print $ find (>100) fibo_lazy_list

I understand that Haskell evaluates only the elements which are required to get the printed results. But are they all kept in memory until the print ? Since only two elements of the list are required to compute the last one, does Haskell release the left-most elements or does the list keep growing in memory ?

vkubicki
  • 1,104
  • 1
  • 11
  • 26
  • Related: https://stackoverflow.com/questions/40562489/why-doesnt-this-haskell-code-exhaust-the-heap – danidiaz Feb 03 '21 at 07:27

1 Answers1

10

It depends.

This is actually one of the most tricky things to get right for real-world Haskell code: to avoid memory leaks caused by holding on to unnecessary data, that was only supposed to be intermediary but turns out to be actually a dependency to some yet-unevaluated lazy thunk, and therefore can't be garbage-collected.

In your example, the leading elements of fibo_lazy_list (BTW, please use camelCase, not underscore_case in Haskell) will not be garbage-collected as long as fibo_lazy_list is refered by something that could still be evaluated. But as soon as it goes out of scope, that isn't possible. So if you write it like this

print $ let fibo_lazy_list = 0 : 1 : zipWith (+) fibo_lazy_list (tail fibo_lazy_list)
        in find (>100) fibo_lazy_list

then you can be pretty confident that the unused elements will be garbage collected, possibly before the one to be printed is even found.

If however fibo_lazy_list is defined at the top-level, and is a CAF (as it will be if the type is not polymorphic)

fiboLazyList :: [Integer]
fiboLazyList = 0 : 1 : zipWith (+) fiboLazyList (tail fiboLazyList)

main :: IO ()
main = do
   ...
   print $ find (>100) fiboLazyList
   ...

then you should better expect all the leading elements to stay in memory even after the >100 one has been extracted.

Compiler optimisation may come in helpful here, so can strictness annotations. But as I said, this is a bit of a pain in Haskell.

leftaroundabout
  • 117,950
  • 5
  • 174
  • 319