Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid stack overflow in Haskell?

Tags:

Haskell does not support cycling for computation, instead it offers to use recursion algorithms. But this approach leads to growing of stack, and even stack overflow. I believe there should be approach to solve this problem in general. Here is the sample. I wanted to know, how many times getClockTime may be called per 5 seconds:

import System.Time

nSeconds = 5

main = do
    initTime <- totalPicoSeconds `fmap` getClockTime
    doWork initTime 1
    where
    doWork initTime n = do
        currTime <- totalPicoSeconds `fmap` getClockTime
        if (currTime - initTime) `div` 10 ^ 12 >= nSeconds
            then print n
            else doWork initTime (n+1)

totalPicoSeconds :: ClockTime -> Integer
totalPicoSeconds (TOD a b) = a * 10 ^ 12 + b

The program goes 5 seconds, but eventually I'm getting:

Stack space overflow: current size 8388608 bytes.
Use `+RTS -Ksize -RTS' to increase it.

Manual management of stack size may help in particular case, but if I would wish to run this algo for 10 seconds, it may overflow again. So this is not a solution. How can I get this code working?

like image 992
Dmitry Bespalov Avatar asked Sep 23 '11 20:09

Dmitry Bespalov


People also ask

How would you avoid a stack overflow?

One method to prevent stack overflow is to track the stack pointer with test and measurement methods. Use timer interrupts that periodically check the location of the stack pointer, record the largest value, and watch that it does not grow beyond that value.

Can Haskell stack overflow?

Haskell can avoid stack overflows in many non-tail-recursive functions because lazy data structures let us put computations on the heap.


1 Answers

The problem here is not the recursion, but the laziness of your n argument. Stack overflows in Haskell come from trying to evaluate deeply-nested thunks; in your case, each call to doWork initTime (n+1) is making a slightly-deeperly-nested thunk in the second argument. Simply make it strict, and things should be happy again: doWork initTime $! n+1.

like image 178
Daniel Wagner Avatar answered Sep 19 '22 15:09

Daniel Wagner