Transcript from the "Proper Tail Calls" Lesson
[00:00:59] And my response was, nobody uses the feature cuz you haven't implemented it, and they say well nobody uses it so we're not gonna implement it. You can see the problem here, right, the catch 22. They don't want to implement something that's hard to implement unless people wanna use it and nobody wants to use it cuz it's not even possible to use.
[00:01:41] Now, just a quick note, there is a related term that if you do some Googling you gonna see TCO, which stands for tail call optimization. This is different and for a long time I actually thought they were basically the same thing, they are different. So let's make sure we're clear, proper tail calls are the idea that a tail call gets memory optimized, that we only use 0(1) memory space essentially.
[00:02:07] PTO, I'm sorry TCO tail call optimization, too many acronyms, TCO tail call optimization is a family of potential optimizations on top of PTC which are optional that an engine may decide that it wants to do. For example, it may decide to throw away old memory, it may decide to garbage collect old stack frames, or it may decide to reuse a stack frame and grow its memory usage, or whatever.
[00:02:57] So the proposal was let's standardize proper tail calls and the language in the spec was deliberately a bit more open to essentially say we're not gonna require specific TCOs we're just gonna generally say that it should be possible for a function in a tail call position to be able to not take up extra memory.
[00:03:57] So here's what PTC was standardized on in ES6. Number one, you have to have strict mode on. It's not going to work if you're in the non-strict mode, sometimes referred to as sloppy mode, you have to opt in to strict mode to be able to do this. By the way, you should already be using strict mode in all of your programs, if you're not, it's time to adopt that cuz that's been around and out for literally 10 years.
[00:04:21] So what are you waiting for? [LAUGH] Let's get on the boat with strict mode. But you have to be in strict mode to be able to take advantage of tail calls. Then you have to have your function call in what is called a proper tail position, it has to be a proper tail call.
[00:04:39] Now, the function call that you see here is in a proper tail position because even though there's an x and 1, and we could even do stuff in the arguments, what you'll note is that nothing has to happen after that function call finishes except for its value to be returned, that's a tail call position.
[00:04:59] It is notable that without the return keyword it is not a proper tail call, even if it's at the end of the function, it's not a proper tail call unless there's a return keyword, okay. Proper tail calls require a return keyword and a single function call, and nothing else in that expression that needs to be computed afterwards.
[00:05:20] There's a slight little grammatical nuance there which is, if you have a ternary expression on your return statement, and the function call is in one of the branches that's a tail call, because nothing else is gonna happen, the ternary precomputes and then picks one of the function calls.
[00:05:37] So that even though it didn't look like a tail call that technically is. But otherwise, generally speaking, the pattern is you're gonna see return and then a function call and that's it and that's gonna mean that it qualifies as a tail call. This has nothing to do with recursion.
[00:06:19] Even though there's math that's happening inside of there the Math.trunc and the x divided by two 2, those are proper tail calls. Notably, Math.trunc is not a proper tail call cuz there isn't a return before it, but it's okay, because Math.trunc doesn't call any further functions, so it's not contributing to the growth of the call stack.
[00:06:39] The thing that would contribute to the growth of the call stack is the call to diminish and that one is in a proper tail call position. So theoretically we should never have a growth of that stack and therefore we should not ever have a range error memory no matter how many times that had to run, okay.
>> Speaker 2: What would happen if instead of Math.trunc we had another function that calls another function? Like something else-
>> Kyle Simpson: That would not, yeah, that's a great question. So if that was a call that would contribute to the growth of the call stack, like if we're calling another function that called another function and created its own call stack growth, then that would definitely potentially grow the stock eventually to create a range error.
[00:07:18] So you only get the benefits of removal of stack frame from a conceptual perspective, if the function calls are all in tail call position. So if you have some that are not and some that are, you're getting the benefit of the ones that are and not the benefit from the ones that aren't, so basic bottom line.