Open micahhahn opened 1 year ago
Thanks for reporting this! To set expectations:
Finally, please be patient with the core team. They are trying their best with limited resources.
I have noticed the same problem before (but forgot about making an issue :man_facepalming:).
While this example here might not seem all that problematic, the CPS (Continuation Passing Style) pattern relies on code like this, meaning that it is not a pattern that we can use in Elm. The pattern IIRC helps with getting stack safety, even when doing operations on the result of a recursive call.
@jfmengels CPS is the same context where I encountered this! Thankfully this doesn't directly block me on anything, it was just a very confusing couple hours of debugging.
For people being bitten by this in the future, using https://github.com/micahhahn/elm-safe-recursion is both going to fix this and allow mutual recusion in TCO
@miniBill While elm-safe-recursion
can be used to work around the bug, the problem still remains that this is easy to unwittingly trigger and extremely difficult to track down.
Here's another example for reference where TCO produces bad closures:
type Trampoline a
= More (() -> Trampoline a)
| Done a
wrapMany : Int -> Trampoline a -> Trampoline a
wrapMany n trampoline =
if n > 0 then
wrapMany (n - 1) (More (\_ -> trampoline))
else
trampoline
run : Trampoline a -> a
run trampoline =
case trampoline of
More next ->
run (next ())
Done a ->
a
The Trampoline
returns More
infinitely and eventually crashes.
However, if you substitute the inline More (\_ -> trampoline)
with a named function:
wrapMany : Int -> Trampoline a -> Trampoline a
wrapMany n trampoline =
if n > 0 then
wrapMany (n - 1) (more trampoline)
else
trampoline
more : Trampoline a -> Trampoline a
more a =
More (\_ -> a)
It's still TCOd but the closures work correctly.
The code generation for the tail call optimization does not appear to create proper closures to variables. In the following example I would expect
goodOutput
andbadOutput
to be the same, but instead all the closures generated intcoMakeLazy
reference the same variable which contains its final value of 3.SSCCE