|
I was talking about the mechanics of the compiler/interpreter, at least the original Sussman/Steele implementation. Let's look at Factorial in Scheme. You mentioned Fib, but Factorial is a simpler example, and will suffice. (define (fact n)
(if (= n 0)
1
(* n (fact (- n 1)))))
The Scheme compiler/interpreter will detect that the last expression to be evaluated, and thus the return value will be: (* n (fact (- n 1)))
Now, you might think that Scheme would detect this as ineligible for TCO, but that's not what happens. In fact, what happens (in pseudo-asm) is this: push *n
push $1
call subtract ;;returns t1 on the stack, taken as arg by fact
call fact ;;both n, and t1, the args to mult, are on the stack, followed by the return address.
jmp mult ;;mult re-uses the stack frame, so it's a tail call.
obviously, in a real asm, sub and mul are primitives, not funcalls, but you get the idea.If that didn't make sense, just read, "LAMBDA: The Ultimate Declarative." it explains it better than I could. So yes, all functions in Scheme are TCOed (at least, in the original implementation), it's just that some of the build of stack frames. I don't know if that's how other languages work, but it might be. |
Specifically, the caller of this function will have to clear out the stack of all operations that were pushed. Something you wouldn't have to do on a tail or non-recursive function.
[1] https://mitpress.mit.edu/sicp/full-text/book/book-Z-H-34.htm...