I found an awesome tutorial on the Internet from NoRedInk about the interoperability on Elm with Javascript and I decided to do it: introducing elm to a js web app.

The tutorial

As is stated at the beginning of the tutorial, it is about integration. The goal is to have a hybrid application which can benefit of Elm’s advantages without upsetting the workflow your developers are used to follow:

When we introduced Elm to our production front-end at NoRedInk, we knew what to expect: the productivity benefits of an incredibly well-built language, and the intrinsic risk of integrating a cutting-edge technology for the first time. Knowing these risks, we came up with a minimally invasive way to introduce Elm to our stack, which let us get a sense of its benefits without having to make a serious commitment.

Wow! Fantastic! This is what I was looking for. The problem is that I’m using the latest version of Elm (0.17) and the tutorial was written ten months ago: in the meanwhile, as expected, breaking changes come together with a new view on the future:

  1. Signals are gone
  2. the module’s declaration syntax changed
  3. the syntax to bootstrap Elm as a module changed
  4. a Port must be declared in a proper port module
  5. Effects are now Cmd

As you know I just started with Elm, so I thought that porting the tutorial and writing about it could be at the same time a great exercise and a good contribution to the community. Here you can find the complete project Flux with elm.

Warning: this guide is supposed to be an update of the tutorial, so follow it first. I assume you are using Elm platform 0.17.

Getting started

So, fork the Facebook’s Flux repository and clone it in your working folder. Then let’s run the TodoMVC app as per instructions:

Then start your own HTTP server like Python’s default server, which listens on port 8000:

Head your browser to localhost:8000 and play a bit with the example. Yes, yet another TodoMVC.

ELM, finally

Declaring module(s)

As soon as you start coding your first module, here’s the surprise.

This:

becomes this:

Cool, now is compiling. Run index.html and… runtime error! How comes?

Actually it turns out that the port needs Json.Decode package to work properly. So let’s add the import in our Ports.elm file:

Declaring Ports

Now we can go further with the tutorial, up to the inbound ports declaration which is now slightly different:

Please note the declaration of a port accepting Unit: my first attempt to update it was using the round brackets notation like this

but causes the runtime error  Uncaught ReferenceError: _Tuple0 is not defined which has already been fixed as of 0.17.1

For now though, we have to use a workaround defining it as

which leads to an awkward usage on the Javascript side, forcing to pass an empty array.

Welcome to Subscription

It’s time to gather all the ports under one subscription. The code used in the tutorial is now unavailable as   Signal.mergeMany and Signal.map  are gone. Instead we use the Subscription as follows:

Note also how the action became a subscription with a proper signature. The aim is always the same, just collecting all the input ports and translating the coming data to typed messages with typed payload which will be sent to through the pipeline to be processed by the update function.

Divide et impera

At this time you probably feel the need to split up your modules. I decided to go for a trivial division of types, models, actions… which helped me while coding. I’m explaining it to let you better understand my repo, if you decide to use it:

  • Main.elm – a simple container for the application, just collecting the submodules and declaring the program
  • TypeAliases.elm – probably the weirdest module
  • Models.elm – the actual model for the application
  • Actions.elm – the allowed Actions
  • Ports.elm – in and outgoing ports, together with the subscriptions
  • TodoStore.elm – the actual module, implementing init and update functions; business logic here

I’m not suggesting you to do the same. It is just the best thing I could do while trying to move the code to 0.17. Feel free to suggest a better practice.

Outgoing port and model changes

At this point, the flow changed compared to the previous one: instead of explicitly managing the updates of the model in the todoListChanges implementation, we just return a Command together with the model in the update function.

In other words, this code:

translates into:

Look at the update’s signature. Now doesn’t really make sense to send the command since we are not transforming it.

Let’s handle the create message so that we’ll be able to see a first working version:

Calling Elm from JS

If you followed the original tutorial, now you are probably at this point (look at js/store/TodoStore.js):

Now, there’s something to fix in the initialization process as it is no more required: get rid of defaultValues and simplify the ports declaration to

Moreover, for some reasons I haven’t yet understood,  sending immediately the message doesn’t work. You need to postpone it with a setTimeout like this:

I put 2 seconds which is even too much, though is just for the sake of testing.

Compile, run and enjoy!

Completing the app

Now you have a first implementation with 0.17, and the business logic is the same as per the original tutorial. Just don’t forget that now your update function is returning a tuple of (Model, Cmd Msg) so you still have to manage it.

To conclude I kindly suggest you to read the great “Takeways” paragraph.

I would add that even if I definitely love Elm, there are a couple of very annoying issues:

  • I don’t like the idea of having to pass an empty array to the dispatcher if I have nothing to dispatch but the action itself
  • while using Elm is painless at runtime, as soon as I tried to interoperate with JS I got a few unexpected runtime errors
  • although the documentation and the community are great, it is still a young and evolving platform with and is difficult to understand all the updates at first sight

Hope that this update will help some newcomer like me to learn faster than I did! Please find here my own version: Flux fork repository

Happy coding.

Credits

Richard Feldman from NoRedInk for the amazing tutorial.

Tobi Vnext who already converted the whole flux example to ELM 0.17 on his repo: his work saved me time understanding Subscription vs. Signal.

MarcoShuttle for letting me know Elm at Codemotion Rome 2016: his presentation has been a kick starter for me!

The great community present on the Elm Google Group.