Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to detect tail call optimization in WebKit?

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?

like image 445
dotnetCarpenter Avatar asked Nov 27 '15 03:11

dotnetCarpenter


People also ask

Does javascript support tail call optimization?

Yes, ES2015 offers tail call optimization in strict mode.

How do you identify tail recursion?

An easy way to tell if a recursive function is a tail recursive is if it returns a concrete value in the base case. Meaning that it doesn't return 1 or true or anything like that. It will more than likely return some variant of one of the method parameters.

How does tail call optimization work?

Tail call optimization happens when the compiler transforms a call immediately followed by a ret into a single jmp . This transformation saves one instruction, and more importantly it eliminates the implicit push/pop from the stack done by call and ret .

Does node support tail call optimization?

Tail-call optimization is a part of the ES2015-ES6 specification. Supporting it isn't a NodeJS thing, it's something the V8 engine that NodeJS uses needs to support. Node 7.10 down to 6.5. 0 support this in strict mode only and with the flag “--harmony”.


2 Answers

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:

  • with TCO: gets shorter, because the stack frame of the outer function gets replaced by the stack frame of the inner function.
  • without TCO: gets longer, because the stack frame of the inner function gets appended.

As of now, this returns true for Safari, and false for other browsers, which is the current TCO support.

"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>
like image 147
zord Avatar answered Oct 09 '22 00:10

zord


Could utilize .toString() , RegExp.test() to check for return statement followed by underscore, or $ sign , a-z characters; followed by open parenthesis followed by any characters followed by closing parenthesis

function forEach(arr, callback, start) {
  if (0 <= start && start < arr.length) {
    callback(arr[start], start, arr);
    return forEach(arr, callback, start + 1); // tail call
  }
}

console.log(/return [_a-z$]+\(.*\)/i.test(forEach.toString()))
like image 34
guest271314 Avatar answered Oct 09 '22 01:10

guest271314