I have a recursive function and exhausting the call stack is an issue I run into sometimes. I know I can use streams, promises with setTimeout, but I would like to just write code that triggers tail call optimization. So far only Webkit seem to have implemented tail call optimization (TCO). Other than knowing the theory, is there any way do check if my code will trigger TCO, either with devtools or by examining the output of the Webkit compiler?
I know it's not perfect, but I think it's the best option we have currently.
Consider this function:
"use strict";
function detectTCO() {
const outerStackLen = new Error().stack.length;
// name of the inner function mustn't be longer than the outer!
return (function inner() {
const innerStackLen = new Error().stack.length;
return innerStackLen <= outerStackLen;
}());
}
Error.stack
is not standard, but it has basic support in all modern browsers. It has different values in different browsers, nonetheless, when a longer named function tail-calls a shorter named one, the stack trace:
As of now, this returns true
for Safari, and false
for other browsers, which is the current TCO support.
In node REPL, Error.stackTraceLimit
is 10 by default, and that is already full by the time detectTCO
is called. So, when the inner stack frame is pushed to the top, the bottom frame is pushed out, so the overall length can become smaller. To prevent this, either increase the limit by Error.stackTraceLimit = 30;
or run detectCTO
with a cleanish stack by calling it from a setTimeout
for example.
"use strict";
function detectTCO() {
const outerStackLen = new Error().stack.length;
// name of the inner function mustn't be longer than the outer!
return (function inner() {
const innerStackLen = new Error().stack.length;
return innerStackLen <= outerStackLen;
}());
}
document.getElementById('result').innerText = detectTCO() ? 'Yes' : 'No';
#result {
color: blue;
font-weight: bold;
}
Is TCO available?
<div id="result"></div>