I did some experimentation today when working on a project and realizing that the new module I added was taking about five seconds to compile, when previously the app would compile in about two seconds.
I did some research and experimentation, and remembered that in order to make the Elm compiler work fast, you want to be making changes most often in files that are imported by the fewest other files. It doesn’t matter so much how many other files a file imports as it matters how many files a file is imported by.
In my case, I had made a new widget, and stuck all of its pieces inside one file. (Where its pieces are its types, the update function, the view function, etc...). I realized that this one file (let’s call it
Foo.elm) was imported by the application’s
Types.elm file. That file happens to be included by a bunch of other files in the application. So when I changed
Foo.elm, it caused
Types.elm to be recompiled, which triggered a re-compilation of a bunch of other files in the project.
As an experiment, I extracted just the types from
Foo.elm in to
Foo/Types.elm. And it cut almost two whole seconds off of recompilations when editing
Foo.elm. I imagine that I could save even more time by splitting out the view, update, and etc... into their own files.
I don’t really like that from an organizational standpoint, but it does speed up the compiler significantly.
FWIW, I had everything in Foo.elm in the interest of following the direction given here: youtube.com/watch?v=XpDsk374LDE. Which I think is good direction, but just putting everything for a section of the app in the same file isn't the best for compiler perf.
I haven't tried this yet, but I'm theorizing that a better approach than extracting types into a whatever/Types.elm file, is to just extract the view functions into their own files as much as is necessary, since the view is typically what is iterated on, and maybe extract the update function too if that's also moving too slowly for you.
Consider the following points:
- Splitting a function out into its own file is only helpful if it doesn't have a parent that's imported by tons and tons of files. If you extract a function to its own file, you might need to extract its parent functions into their own files as well if you want to see an improvement in compilation speed.
- Where possible, functions that are used in many places should be moved into their own files for the sake of performance, and should not import any files that you will be updating regularly.
- This is a trade-off game, dealing with tons of files can be a pain. It's probably a bad idea to go all crazy and start putting all of your functions into their own files right off the bat.
Photo by Veri Ivanova on Unsplash