The Elm Architecture Flow
What does the Elm Architecture look like? That’s what I immediately thought, when I started to read about the language. Currently I like Elm quite a bit because it makes it so easy for me to do some application programming with HTML UIs. I never got the hang of JavaScript and one its myriad frameworks. I strongly believe there’s something fundamentally wrong with the current JS approach. HTML used to be simple, straightforward. Embellished with some JS behind the scene it still was ok. But today…? Somewhere along the way the JS community took the wrong turn.
But, well, with Elm the fun is back in browser application development. Or maybe I just write a small frontend with Elm to talk to a C# or Ruby backend? We’ll see. Right now Elm’s enough to study. A very nice Functional Programming language.
If you like, step through Elm’s online documentation. It gives a good overview of the platform's philosophy and capabilities. You can even play around with example code in an online REPL.
Despite all the effort that went into the explanations of Elm, though, I did not find it easy to understand how the often cited Elm Architecture actually works or looks. Visualizations are strangely absent from the whole documentation.
Or maybe not that strangely, because it seems to be a pattern: Functional Programmers don’t use diagrams. Object-oriented programmers have to use diagrams – e.g. class diagrams for structure and sequence diagrams for behavior – since OOP is so messed up. You can’t really make sense of OO code without diagrams. But FP code… that’s a totally different matter altogether. FP code is by definition easy to understand and reason about. Or so the thinking seems to go.
However, I beg to differ. I even think the FP community does itself no favor by focusing on text (program code) so much.
That’s why I’d like to draw some diagrams in this article. I want to translate the Elm Architecture into pictures for even easier understanding. And hopefully you’ll agree that depicting FP is easy and worthwhile.
Show some HTML
The easiest thing you can do with Elm is to just output some HTML. No domain logic, just pure declarative HTML rendering:
I'm sure you understand this code right away. main is the entry point of this small app, and h3 is a function (!) with two parameters (which are lists as denoted by the [...]). h3 and also text ultimately generate HTML.
As you can imagine there are lots of similar functions in Elm's core library Html. One for every HTML element and many of their attributes. And so you can compose HTML element hierarchies by arranging Elm function calls in a like hierarchy.
And here's the actual Elm programm running:
Yes, this output is created by an embedded Elm program right here in this page. That way I was able to mix Wordpress text composition with Elm functionality.
But can I visualize the Elm Architecture of this application? Yes, I guess I can. Here it is:
main is a function without parameters which gets called by the Elm runtime and returns HTML. At least in some way, because to be more accurate, it's not HTML which gets produced by h3 and its friends, but some kind of HTML DOM. But I think the difference does not really matter for the purpose of this article.
Calling a function can be understood as processing input and producing output. Some data flows into the function, some data flows out from the function. A function thus is nothing more than a transformation step in a data flow.
The diagram above is trying to make this more tangible. You don't have to interpret program code but can see what's going on at a glance. A mental model is much easier to build that way. That's a boon for collaborative software development. Mental models of a team members can be synchronized in less time and with less misunderstandings.
Becoming interactive
Rendering some HTML is no big deal with Elm, but also of not much help. Elm starts to shine when users are supposed to interact with the HTML.
Let me enhance the Hello-World-scenario a bit: Now the user should be greeted personally with his name. For that she has to enter her name and the application has to modify the message accordingly. Try it out, here's a running Elm app implementing these requirements:
As you can see the message is updated with every keystroke you type in the input box. Pretty cool, isn't it? ;-)
And here's the Elm code:
It represents most of what's called the Elm Architecture. Just notice the model, view, and update mentioned at the top. Sounds like MVC, but it isn't. Maybe I should call it the MVU pattern?
It can't be MVC because MVC is based on functional dependencies. But there aren't any in MVU. The functional units view and update do not know each other. update does not call view - nor the other way around. That's fundamentally different from MVC.
Elm thus follows what I call the Principle of Mutual Oblivion (PoMO, read more on that in my eBook): functional units working together towards a goal do not know each other and that way do not functionally depend on each other.
update and view just share a data contract: model and Requests. Both flow as data (messages) between the two functions:
That's the essential Elm Architecture data flow. How long did it take you to reverse engineer that from the textual code? Or would have taken? At least me it took some time - which, sorry to say, is a waste. It's wasting precious developer minds to have them decode and reverse engineer textual code when a picture can convey the same with less words and less ambiguously.
What about main you might ask? main is still there, but it's doing nothing anymore. Its sole purpose now is to integrate view and update with the help of Html.App.beginnerProgram. Elm thus is also following what I call the Integration Operation Segregation Principle (IOSP). It states: a function either contains logic or integrates. Together with PoMO this leads to code free of functional dependencies.
main is wiring up the fundamental data flow loop:
The "Elm cloud" is where HTML gets actually drawn and interactions with the HTML get translated back into the Elm world. For example when a user types into the input box the onInput function is called and produces the UpdateName message which is send together with the model as a request to update.
Also Elm is responsible for keeping the model around. view and update are pure functions; they are stateless. But after view
did its thing with the model flowing in it's not flowing out. So how come the model can flow on into update? Because Elm is keeping it around.
All data is immutable in Elm. So view cannot change the model. That's update's task. It produces a new model upon each request which flows again into view to be displayed – and is memorized for the next roundtrip by the Elm runtime.
The model lives across interactions. The union type Requests stands for what's new for each interaction: a message from the user to update. The Elm documentation calls this type mostly Msg. But I find it more appropriate to name it Requests, because that's what's happening here: the user requests some behavior from the application.
What kind of behavior that might be is defined by the type's values. In this case it's just one: UpdateName String.
Doing impure stuff
This kind of architecture already serves the purpose in many cases. But Elm is a real Functional Programming language. It tries to shield you from dirty side effects. All functions are pure. Each time you call them with the same input they produce the same output. That would defy the purpose of a function to return the current time, though. It's supposed to return a different value each time it's called with the same input (which is empty).
That's why getting at the current time is not really straightforward as in JavaScript or C#. There is no DateTime.Now function. Rather you need to command the Elm runtime to send you a message with the current time.
Here's an updated version of the Hello-World-program: it will greet you with a different message depending on the time of day.
And this is the code behind it:
The basic architecture is the same: there is view, there is update, there is a model, there are Requests. But now there is not just a model flowing between the functional units but a model and a so called command.
Commands are requests directed at the Elm runtime; they contain functions to be executed not directly, but behind the scenes, and whose result then get's passed to update as another request.
Check out init: It not just initializes a model but also builds a command. In this case that is a backround task to retrieve the current time. Once the tasks completes the time value flows out of the runtime as a SetGreeting message.
The updated Elm Architecture looks like this:
Although model and command flow out of init and update as a tuple together I like to draw separate data streams for them. (Since Functional Programming does not use diagrams FP solutions often are pretty much stuck with functions to produce a single output for each input, although two different outputs are meant. I find that limiting. Data flow diagrams can express much more than that. But that's a topic for another day...)
Back to the Elm Architecture: Once you start with impurity commands enter the scene. Even if you don't want to command the runtime to do something you need to output a command: Cmd.none.
Typical commands are background tasks or creating a random number or sending a HTTP request. Elm calls this "managed effects".
Listening to events
And finally: What if you don't know when something interesting will happen upon which update should react? There is event handling build into Elm's runtime.
Whenever a model flows through the system you can register so called subscriptions. In the previous example there were none, but still I had to state that explicitly by defining a lambda function \n -> Sub.none.
In the final version of the Hello-World-program I want to change that. Why not change the color of the greeting depending on the current mouse position? Try it out:
The code does not look that much different. The devil in the details is only a small devil ;-)
Most important is the function subscriptions close to the end. It returns, well, a subscription to mouse move events. Each time the mouse changes its position a RegisterMousePosition message is send to update. It then updates the mouseX value in the model which view transforms into a color.
Please note that the subscriptions can change each time the model flows out of init and update. That way they can be switched on and off at any time. To subscribe repeatedly to the same event does no harm, though ;-)
***
That's all of the secret of the Elm Architecture as I understand it. Simple, isn't it? But only easy to understand once you get a picture of it.
Interestingly "architecture" in this case is not associated with structure like in class diagrams or the layered architecture pattern or even MVC. This time it's about communication, about data flow. That's because Elm is a Functional Programming language. It focuses on behavior, i.e. that which a customer or user is interested in most. Software is a bunch of processes transforming data from the environment reaching it through triggers, the result of such processing: behavior expressed by more data. Software essentially is about data transformation processes. And that's expressed best with data flow diagrams, I'd say.
That said I'd like to encourage you to take a look at Elm. It really puts the fun back into web programming. And since it's a UI oriented language/framework it's a nice way to play around with FP concepts. Makes FP more tangible; you can do more fun example programs.
And if you're not sure which toy problem to tackle, check our the Clean Code Developer School's coding dojo with more than 70 code katas on different levels of difficulty.