Reported as a side note in #32: a long enough do block fails at runtime. The failure is not an actual stack overflow during evaluation, it happens before any code runs. Lua refuses to parse the file: lua: golden.lua:144: chunk has too many syntax levels.
The cause is the shape of the generated code. Each statement of a do block compiles to a bind continuation that is lexically nested inside the previous one:
return M.discard(M.Effect_Console_foreign.log("61"))(function()
return M.discard(M.Effect_Console_foreign.log("62"))(function()
return M.discard(M.Effect_Console_foreign.log("63"))(function()
...
Lua's parser has a hard recursion limit (LUAI_MAXCCALLS, 200 by default), so a do block with roughly 200 statements produces a chunk that no stock Lua interpreter will load. The repro is mechanical:
main :: Effect Unit
main = do
log "1"
log "2"
-- ... 300 lines total
log "300"
To make this an eval golden, generate the module and the expected output:
cd test/ps/golden/Golden && mkdir -p LongDoBlock
{ echo 'module Golden.LongDoBlock.Test where'; echo
echo 'import Prelude'
echo 'import Effect (Effect)'
echo 'import Effect.Console (log)'; echo
echo 'main :: Effect Unit'
echo 'main = do'
for i in $(seq 1 300); do echo " log \"$i\""; done
} > LongDoBlock/Test.purs
mkdir -p ../../output/Golden.LongDoBlock.Test/eval
seq 1 300 > ../../output/Golden.LongDoBlock.Test/eval/golden.txt
This is a different root than the instance dictionary overflow fixed for #32, which is why it gets its own issue. Fixing it means making the generated code lexically flatter, and none of the options are local tweaks:
- A magic-do style rewrite for Effect, like the upstream JS backend: turn chains of
bind/discard over the Effect monad into sequential statements inside one function body. Most effective, but requires recognizing the Effect dictionaries.
- Hoisting continuation lambdas into named locals. This only helps for
discard chains where continuations capture nothing; a x <- m bind captures x, so the continuation cannot leave the enclosing scope, and the nesting stays.
- Documenting the limit and recommending users split long do blocks. Cheap, but it leaves generated code that a stock interpreter cannot load, and the prelude test suites hit it in practice.
Reported as a side note in #32: a long enough do block fails at runtime. The failure is not an actual stack overflow during evaluation, it happens before any code runs. Lua refuses to parse the file:
lua: golden.lua:144: chunk has too many syntax levels.The cause is the shape of the generated code. Each statement of a do block compiles to a bind continuation that is lexically nested inside the previous one:
Lua's parser has a hard recursion limit (
LUAI_MAXCCALLS, 200 by default), so a do block with roughly 200 statements produces a chunk that no stock Lua interpreter will load. The repro is mechanical:To make this an eval golden, generate the module and the expected output:
This is a different root than the instance dictionary overflow fixed for #32, which is why it gets its own issue. Fixing it means making the generated code lexically flatter, and none of the options are local tweaks:
bind/discardover the Effect monad into sequential statements inside one function body. Most effective, but requires recognizing the Effect dictionaries.discardchains where continuations capture nothing; ax <- mbind capturesx, so the continuation cannot leave the enclosing scope, and the nesting stays.