-
-
Notifications
You must be signed in to change notification settings - Fork 758
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement mutual recursion TCO on JavaScript #3830
Comments
It cannot be a circle of 3 functions? If it can, does Erlang support this? |
It would support any number of mutually recursive functions. |
Something is wrong with the gift wrapping machines at Santa's facilities in Swindon. You're the lucky elf who's been selected to take a look and fix it! Louis, the lil british Elf, gives you a schematics of the Gift-Wrapp-inator and with great horror you discover it's powered by Gleam code! Upon further inspection you notice that these two functions are the source of the problem, causing the machine to stack overflow when there's 1_000_000_000 of gits to wrap: pub fn wrap(gifts) {
case gifts {
[] -> we_can_go_home()
[gift, ..gifts] -> do_wrap(gift, gifts)
}
}
pub fn do_wrap(gift, rest) {
case is_fragile(gift) {
True -> bubble_wrap(gift)
False -> Nil
}
wrap_with_paper(gift)
put_christmas_cockade(gift)
wrap(gifts)
} It looks like some naughty elf has not been careful enough with their recursive functions! There's no time to change the Gift-Wrapp-inator code (Santa is really slow at code reviewing), so the only solution left to save Christmas is to implement proper tail call optimisation for mutually recursive functions! |
Am I the only one that can see that message? It feels targetted |
Would this also address cases like the following which overflow the stack on JS even though only a single top-level function is defined? import gleam/bool
pub fn main() {
go(10_000)
}
fn go(i: Int) -> Int {
use <- bool.guard(i < 0, -1)
case i {
0 -> 0
_ -> go(i - 1)
}
} The anonymous function created by Alternatively, with some extra smarts could the codegen for The above Gleam would then generate the following JS: export function go(i) {
if (i < 0) {
return -1;
} else {
if i === 0 {
return 0;
} else {
return go(i - 1);
}
}
} Just an idea at this stage! |
I don't think the compiler can do this as It would require full program compilation to know it can inline |
Ok, so fixing the stack overflow on JS caused by a Shall I open a separate issue? |
If we implemented function inlining, as discussed in #3722, in addition to mutual recursion TCO, that would fix the issue. However, neither of those are a small task and it's likely going to be a while before we get them |
I don't think we would be able to solve that with mutual recursion TCO as it's not mutual recursion, It would be solved by inline though: #3722 edit: oops, I now see that I am too late to this conversation! ahaha |
V8 (the most widely used JS engine) doesn't implement tail calls for some bad reason, so we need to implement tail call optimisation in the compiler.
Today we support self-recursion. Extend this to support mutual recursion.
My thought was that we could compile this code
To something like this
The exact names etc we would use would need some thought, but you get the idea.
We already identify functions reference loops during analysis, so perhaps we could use that as a starting point for identifying when this optimisation can be applied.
This would be a challenging item of work.
The text was updated successfully, but these errors were encountered: