21

How can I "kill" a pure calculation which is taking too long? I tried

import System.Timeout

fact 0 = 1
fact n = n * (fact $ n - 1)

main = do maybeNum <- timeout (10 ^ 7) $ (return . fact) 99999999
          print maybeNum

However, this doesn't work. Replace the (return . fact) 99999999 with a "real" IO function like getLine and this works as expected.

amindfv
  • 8,438
  • 5
  • 36
  • 58
  • Interesting that if `fact` become "real" IO action (`fact 0 = return 1; fact n = (n *) \`fmap\` (fact $ n - 1)`) then `timeout` works as expected too. – Matvey Aksenov Apr 09 '12 at 10:04
  • @MatveyAksenov: I think that's not so much because the function is a real IO action, but because its recursion is, by means of `fmap`, moved into the `IO` monad. – leftaroundabout Apr 09 '12 at 10:11
  • 2
    If you need to timeout arbitrary and potentially malicious code, make sure to test against really simple non-allocating loops, e.g. `let x = x in x` and `let x () = x () in x ()`. – Ganesh Sittampalam Apr 19 '12 at 11:28

1 Answers1

23

The point is that

return (fact 999999999)

immediately returns and doesn't trigger the timeout. It returns a thunk that will be evaluated later.

If you force evaluation of the return value,

main = do maybeNum <- timeout (10 ^ 7) $ return $! fact 99999999
          print maybeNum

it should trigger the timeout (if you provide a stack large enough so that the timeout happens before the stack overflow).

Daniel Fischer
  • 181,706
  • 17
  • 308
  • 431