0

Consider the following return statement:

return f() || g();

The call f() obviously is not a tail call, because the function does not actually return if f() is falsy.

What about the g() part though, is that a tail call? Or do I have to rewrite it like this:

const temp = f();
if (temp) return temp; else return g();
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • 1
    Try it and see? "Do I have to ..." <= easy enough to test the different cases to see if you "have to" – Taplar Jan 21 '21 at 22:46
  • It doesn't matter in practice. Only Safari actually supports tail call optimization, no matter what the standard says. – user2357112 Jan 21 '21 at 22:49
  • 1
    JS coders shouldn't worry about implementation details, which frequently change anyway; what's the actual problem/question here? – dandavis Jan 21 '21 at 22:49
  • 1
    @dandavis: It matters for tail call elimination. – user2357112 Jan 21 '21 at 22:50
  • 2
    @dandavis: Tail call elimination allows program designs that don't work without tail call elimination. It's not an implementation detail. – user2357112 Jan 21 '21 at 22:53
  • @dandavis I just want to make sure `function generate() { return attempt() || generate(); }` does not blow the call stack. – fredoverflow Jan 21 '21 at 22:55
  • fair enough, but I wouldn't count on it anytime soon. – dandavis Jan 21 '21 at 22:55
  • @dandavis: It's standard-mandated, but support is terrible. – user2357112 Jan 21 '21 at 22:58
  • I would think if you want to make sure you do not blow the stack you need to work in logic to account for a max recursion count. For instance you could pass the count into generate, default it to zero, and increment it on each subsequent call. Once it reaches whatever number you deem too much, stop the nested calls. – Taplar Jan 21 '21 at 23:06

2 Answers2

2

Yes, but it doesn't help in practice.

According to the standard, g() is in tail position:

LogicalORExpression : LogicalORExpression || LogicalANDExpression

Return HasCallInTailPosition of LogicalANDExpression with argument call.

However, most browsers don't support tail call elimination, and the Chromium team isn't working on it, so you can't rely on tail call elimination in practice no matter how you write your tail calls.

user2357112
  • 260,549
  • 28
  • 431
  • 505
1

Try it and see?

For some reason that didn't occur to me in my sleep-deprived state :D

function f() {
    return Math.random() > 1 || f();
}

f()

Node says RangeError: Maximum call stack size exceeded, Firefox says InternalError: too much recursion.

fredoverflow
  • 256,549
  • 94
  • 388
  • 662