31

I need a loop for main in Haskell. I've tried this:

main :: IO ()
main =
  do
    putStrLn "do something"
    main

Is the above code the correct approach to take? Will this infinite recursion cause an overflow?

2 Answers 2

40

This is fine; no stack overflow will occur. Stack overflows in Haskell (and, indeed, any non-strict language) are different to those in other languages; they arise from accumulating large values without ever evaluating them, but you're not accumulating anything here; just sequencing an infinite chain of actions. You can think of it like this: Once the line is printed, the action is discarded, and control passes straight onto main; nothing is kept on the stack because there's nothing that has to be returned to.

It's the same reason you can iterate through an infinite list without running out of memory: as the program goes further down the list, previous list cells are reclaimed by the garbage collector as they're no longer required. In this case, the previous sequencings are reclaimed, as there's no reason to keep around an action you've already performed.

That said, a nicer way to write this particular example is:

import Control.Monad

main :: IO ()
main = forever $ putStrLn "do something"

Of course, this won't work if your loop is ever intended to terminate. forever is itself implemented with recursion, so there's no benefit other than readability to doing this.

Sign up to request clarification or add additional context in comments.

7 Comments

why no stack overflow will occur if I do so? what's Haskell's recursive different from C's
Stacks are different because Haskell (like several other languages) guarantees "tail calls" do not allocate stack frames: en.wikipedia.org/wiki/Tail_call
@ChrisKuklewicz: Actually, Haskell doesn't guarantee tail call optimisation, and in fact it's perfectly possible to write a tail-recursive Haskell program that causes a stack overflow. But there's a closely-related concept that does apply, commonly called tail recursion modulo cons. Still, even then it's just an implementation detail; the Haskell report doesn't specify any details of evaluation strategy at all.
Furthermore, the main call in main is not in a tail position, because the definition is main = (>>=) (putStrLn "do something") main. So it is (>>=) which is tail called by main. The definition of (>>=) in the IO monad may or may not tail call its second argument. That said, I'd expect the recursion to run without growing the stack.
augustss, should that be (>>)?
|
2

What you observe is the result of "tail call elimination / optimization", which is nothing special to Haskell. See e.g. http://en.wikipedia.org/wiki/Tail_call

1 Comment

Actually it's a bit different in Haskell, due to laziness. Some things that wouldn't be optimized in strict languages are able to be optimized in Haskell. However, if you have a strict Haskell function, then TCO behaves roughly the same as in strict languages that implement this feature.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.