1

I wondered that every function in Haskell should be tail recursive.

The factorial function implemented as a non tail recursive function:

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

Every operator is a function too, so this is equivalent to

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

But this is clearly a tail call to me. I wonder why this code causes stack overflows if every call just creates a new thunk on the heap. Shouldn't i get a heap overflow?

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
Karroffel
  • 115
  • 6

1 Answers1

2

In the code

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

the last (*) ... is a tail call, as you observed. The last argument fact (n-1) however will build a thunk which is immediately demanded by (*). This leads to a non-tail call to fact. Recursively, this will consume the stack.

TL;DR: the posted code performs a tail call, but (*) does not.

(Also "the stack" in Haskell is a not so clear notion as in strict languages. Some implementations of Haskell use something more complex than that. You can search for "push/enter vs eval/apply" strategies if you want some gory details.)

Sibi
  • 47,472
  • 16
  • 95
  • 163
chi
  • 111,837
  • 3
  • 133
  • 218
  • So it's the nature of the `(+)` operator. Maybe the `(+)` implementation keeps the value on the stack and that's why i get a stack overflow and not a heap overflow – Karroffel Mar 30 '15 at 08:29
  • 1
    @dosenfrucht, thinking about tail calls in Haskell may lead you astray, because Haskell's evaluation model is very different from Scheme's or ML's. However, the fact that `*` is strict in its second argument means that you will face the same problem as a result of `fact (n-1)` not being in tail position as you would in a strict language. – dfeuer Apr 02 '15 at 01:09