I finally managed to (hopefully) solve the stack overflow problems when running my port of the Elm compiler on other browsers than Safari.
With the new version I can compile the big module of the elm-default-tailwind-modules package in my Chrome browser, and I can compile the compiler with itself, too There still might be problems with other big projects, though, so please tell me if you happen to encounter a stack overflow or other errors.
If you’re interested in the details of the changes I applied, feel free to ask.
In the Elm Slack I was asked what I had changed. I’m answering here because the posts stay longer.
Here is a short list:
In the original compiler, types like Parser, Decoder, Result, Tracker are implemented using a technique which is called (I think) “Church Encoding”. I wanted to add something like a stack-safe loop function to some of these types, but didn’t know how to do this in the Church Encoding, so I changed the defiinition of these types. For Decoder and Tracker this was all I had to do to solve the stack overflow problems there.
For Parser, Result and my IO monad I added the said “loop” function and used them where needed. I also used them to add some optimized fold and traversal functions to Result and IO.
In some places, the Elm compiler builds a graph (of nodes and edges) and then uses a library to dermine the so-called “strongly connected components” of such a graph. I had ported the code from the Haskell library to Elm, but as it turned out, that hasn’t been stack-safe. I had to re-implement the graph algorithm from scratch.
Finally, there were some functions where I had to manually try to make them tail-recursive, sometimes using a so-called continuation passing style: the functions constrainDecls and solve from the type checker, and the function checkDecls from the nitpicker. I was also lucky that Jeroen Engels showed me in his pull request how to make my implementation of the MVector type tail recursive. Thank you again @jfmengels.
This is just a very high-level description of my changes. If you want to know more, I’ll try to find some time to describe some points in more detail.
I was also asked whether these changes had any impact on performance, but after some very rudimentary testing I can’t see any substantial difference.
Thank you for making this even better and finding ways to not exceed stack limits on Firefox and Chromium.
Do you maybe have a basic introduction to this maybe-named “church encoding” technique?
Is it similar to defunctionalization, where instead of stacking function executions one returns records that are then handled in later runs, like e.g. in elm/parser loop?