Greedy Elm

At the end of May, we gave a talk about Elm and JS interop at Prague Elm Meetup. One of the sections naturally talked about native modules and their dangers, so it shouldn’t come as a surprise that they came to bite us just a few days later.

The initial manifestation was that some parts of the Elm application were using the wrong copy, more specifically a wrong language of the copy. It wasn’t the whole application, just few buttons here and there. On top of it, some of the elements with the wrong copy didn’t even appear until after some actions were taken.

Our current setup is an Elm application embedded into a larger JS context. The Elm application operates mostly independently apart from being started by the JS, but there are few domains which are shared between Elm and JS. One of them is internationalization. We’ve had JS implementation long before Elm came in and it doesn’t make sense to duplicate it in Elm. To leverage the JS implementation, we wrote a wrapper in Elm, a native module, around it so that Elm can call it directly and synchronously. We are aware of the risks of this approach but native module here makes sense and it has been working out great. So far.

Based on the result we were seeing, it was apparent that i18n module is to blame. As a sanity check, we went to check the contact surface of Elm and JS. First, we verified that Elm sends the keys to be translated to the native module and it’s not a typo. Second, we checked that the native module sends the correct values to JS because there is a JSON encoder for advanced options. Third, we checked that the JS library receives the same keys for translation.

Everything was ok up to this point except for the order of steps. We clearly saw the problematic texts to be translated as the very first keys, way before they were needed by the UI and before other related copy appeared. This realization lead us to the immediate cause of the problem: the strings were being translated before the proper locale wa set in the i18n library. Global state was to blame!

We tried to translate the strings from console and then from other parts of the Elm app - it was fine. So the sequencing of the operations was to blame, but how was it possible? We load the application code (including Elm), set locale, and only then start the Elm application. How can anything travel back in time?

After some head scratching it finally dawned on us: Elm is greedy! The mis-translated strings appeared in view functions which didn’t have any arguments. Elm runtime evaluates these functions as soon as it gets a chance and uses the result later instead of re-evaluating the function because all functions in Elm are pure and repeated evaluation with the same (e.g. none) arguments will always give the same result. Unless it uses native modules dependent on global state.

The takeaway is: beware of native modules and be ready for some head scratching.

As for our problem, we re-ordered the operations so that locale is set as early as possible, before Elm is even loaded. It’s not a perfect solution, but it works as long as we don’t switch locales within one page render.

We're looking for developers to help us save energy

If you're interested in what we do and you would like to help us save energy, drop us a line at jobs@enectiva.cz.

comments powered by Disqus