Using Elm in the newsroom

Strong typing and a helpful compiler make it useful for rapid development — but it’s not mature enough to replace JavaScript

As members of the small news development team at The Times and The Sunday Times building a lot of software at high speed, we face a very particular set of challenges. Because we work on everything from internal tools to interactive stories and email newsletters, we have to learn a wide range of technologies. We also have to switch between them frequently: it’s not unusual to work on three different projects in a day. And we have to be fast, working to deadlines that change as often as the headlines.

As such, we’re always on the lookout for tools that can speed up our development or make our lives easier. The latest of these that I’ve tried is a new programming language called Elm.

Elm is a functional, statically-typed language for front-end web development. It brings with it everything that’s nice about those paradigms, such as purity, immutability and type checking, and then compiles to JavaScript. You can think of it as a replacement for React or other frameworks, though it is a separate language in its own right.

After reading up a little, I decided to dive right in and use it for a week to build a relatively simple, standalone project: our guide to the Premier League transfer window. Here’s what I found.

First of all, it has to be said that some things about Elm are great. There are all the usual benefits of functional programming, of course: pure functions (which rely only on their parameters and don’t have any hidden side effects) make it easy to work through your program’s logic and lead very naturally into unit testing, while immutable data structures help avoid sneaky bugs.

Then there are the advantages of static typing. Because every function and piece of data must be explicitly labelled with its type, Elm’s compiler is able to analyse your program and catch problems before they happen: no more accidentally treating strings as numbers, or “undefined is not a function”! This helps avoid a whole class of errors.

Indeed, Elm’s compiler is where the language really shines. A lot of work has gone into its helpful error messages, with the result that it’s a real pleasure to work with. Writing a case (switch) statement and forgot to account for a possible value? No problem:

Still finding your way around the language, or just prone to typos? Elm’s compiler has you covered with useful suggestions:

In addition to the built-in types, Elm allows you to define your own. For the transfer window project, for example, I set up a data structure to represent a team:

type alias TeamData =
{ author: String
, copy : List CopyData
, name : String
, transfers : List TransferData

So a TeamData object (or record in Elm parlance) contains an author (the journalist who wrote the copy), a list of copy objects, the name of the team, and a list of transfers. It’s easy to read, easy to understand, and means I can simply use the alias TeamData instead of writing out that whole data structure every time.

For example, here’s a simple function that takes in a team and returns some HTML displaying the team name and author. The first line is the type annotation, which shows that the function is expecting a parameter of type TeamData and will return HTML; the rest is the function body:

displayTeamName : TeamData -> Html a
displayTeamName team =
div [ class "team" ]
[ div [ class "name" ] [ text ]
, div [ class "author" ] [ text ]

All of this makes refactoring — something we do a lot when dealing with rapidly changing news stories — a breeze. I could move code around with confidence, knowing that my custom types were easy to follow and Elm’s compiler would catch any errors I introduced.

Elm development does have its downsides, however. It’s not the language’s fault, but the fact it’s so new (v0.17 came out only in May 2016) means it can be hard to find guides or code examples that are both useful and up-to-date. The official website has a helpful introduction to the basics but doesn’t go into much detail on more advanced topics, while the documentation for the core and HTML libraries is excellent for reference but no substitute for a good tutorial.

The nascent community, meanwhile, means production-ready, third-party packages are in short supply. In recognition of this Elm has ports, which allow JavaScript libraries and Elm code to interoperate. Here’s an example of a port on Elm’s side, which is listening for a list of JSON values:

port transfersJson : (List Json.Value -> msg) -> Sub msg

And on the JavaScript side:

const transfers = [
team: 'Arsenal',
player: 'John Smith'

In our experience this approach worked well for a library like Tabletop, which could simply grab some data and send it to Elm as above, but we struggled to integrate more complex libraries like HammerJS, which requires knowledge of the DOM that Elm is rendering.

Speaking of JSON, Elm’s approach to decoding JSON strings into usable data is… tricky. The lack of clear tutorials was the biggest culprit here, but it took me quite a while to figure out the relationship between data, decoders and then functions that actually run decoders. While it makes sense in Elm to have to explicitly state the type of each JSON property and account for potential decoding failures, it really felt like a hassle compared to a simple JSON.parse in JavaScript — especially for our data, which came from an internal source with near-enough guaranteed correctness.

Finally, the Elm ecosystem lacks support for using the language in production — probably because so few organisations have adopted it yet. One problem I ran into was that elm-reactor, the core live-reload system for development, doesn’t play nicely with ports, so we had to switch early on to manually running the compiler every time we changed our code. (Evan Czaplicki, the creator of Elm, has said future releases will put more emphasis on production.)

Overall, Elm seems poised to bring a lot of advantages to the kind of rapid development we practice in the newsroom. Its strong type system and helpful compiler caught a lot of the silly errors we often introduce when working at speed, while the MVC structure it encourages (the “Elm architecture”) meant I didn’t have to think too hard about how to build the app. In fact, coming from JavaScript’s tooling madness, having a single way to do everything in Elm (one live-reload tool, one compiler, one structure) was a breath of fresh air.

Would I use it again in production? Maybe. It’s a compelling proposition, and for small projects like the transfer window interactive it seems as good an option as React (which it claims to outperform) or anything else we currently use. For larger scale products we’ll probably stick to JavaScript for the time being — but ask me again in a year, when the language and community have had chance to mature, and the answer could be very different.

In the meantime, we’re doing our bit by open-sourcing the Elm code for our transfer window project: you can find it on Github here.