Introduction Into Elm (Pt.2)

Part 2: The Elm Architecture (TEA)

A. Sharif
JavaScript Inside
Published in
11 min readMar 9, 2017

--

“Introduction into Elm” is aimed at developers who might have a JavaScript background or a very specific React background or simply want to get started with a functional language on the front-end. If you have been playing around with and/or have been thinking about getting started with Elm, this writeup is intended to help you gain a deeper understanding of the language.

At the end of this course you should have a fundamental understanding of what Elm is, how to build applications with Elm and be ready to tackle more advanced topics like building your own modules or packages. So why even try to write an introduction to Elm then? Aren’t there enough resources out there that cover the basics? Maybe, but there can never be enough resources when a language hasn’t gained mainstream appeal yet and furthermore it’s a great way to reflect on one’s own understanding of the topic.

We will gradually move from covering the very basics to building a full functioning application. I plan to cover this in a multi-part series. Every part being a small chapter, where we tackle a specific topic.

Part 2, “The Elm Architecture (TEA)”, will mainly focus on building our first small examples and gradually start to cover more advanced topics explaining the Elm Architecture (TEA) along the way.

In case you have missed out on part 1 or want to recap on the very basics checkout “Part 1: Getting Started For The Absolute Beginner” first. Just to quickly reiterate on the first installment, we went through how to set up Elm, render a basic string to the screen, learned know how to work with the REPL and should have a basic grip on the most fundamental data structures.

Basic Examples

To set things off, let’s write the classic Counter example. To follow along you can either run Elm locally (also see the Basic Example section in Part1 for a detailed walkthrough on how to install Elm) or use the online editor available at http://elm-lang.org/try (recommended for starters) or the more advanced editor at https://ellie-app.com.

Let’s begin with writing a new file called Counter.elm.

Let’s define a module Counter and import Html as well as Html.Events and expose onClick. Taking a quick a look at the html package, we notice that the package includes Html, Html.Attributes, Html.Events and other modules. Html.Events offers a number of mouse as well input helpers. We’re interested in capturing any click events in this specific case, that’s why we imported onClick.

Every module in Elm has a main function, so let’s define our main function by using the beginnerProgram function in the Html module.

We will get into more detail about what model, update and view are, but let’s define our model first.

In our specific Counter example we’re interested in the actual count which is in an integer. So the type alias for our Model is Int. This looks trivial at first glance, but imagine we’re dealing with larger structures. Let’s take the following example, where we’re dealing with todo items and have a function toggleTodo. How would the type annotation for toggleTodo look like?

This is hard to read already, now imagine we have a record with even more fields. Now what if we would define a type alias Todo?

By defining the type alias, we can suddenly rewrite the type annotation to:

So back to our Counter example. Now that we have defined a type alias, let’s also define our model.

We will see more advanced example regarding model, but for now we should have a basic understanding of how to define the model.

Next, we’ll need to take care of defining how we react to changes in our App. For that, we will need to define Messages and an update function that declares what happens when we receive one of the defined messages.

So we have defined a type Msg using a union type. Union types are useful when we need to describe complex data. You will very often come across union types in Elm. Just take a look at the following short example.

We can express a common state inside our application, where we might not have started to fetch yet or might be currently loading the data. This enables us to write a function that expects a DataSet and knows how to handle all specific cases. So suddenly we can write a view function that expects a type DataSet like this:

We will come across union types again, when we start building our application.

But what are Messages? These are things that can take place in our application. In this specific case we know that a user might want to increment or decrement the counter. So by declaring our type Msg, we list all the possible messages that we want to respond to. This is also where the update function comes into play, as we have to specify what happens when we receive any of the messages.

As you can see, we defined what happens when we receive an Inc and a Dec. If you know redux, this might remind you of a reducer, we can clearly see the influence of the Elm architecture on redux here. Another interesting fact is that we have to handle all possible messages else the Elm compiler will complain. This is another strength that Elm has to offer, you can’t leave out any messages, everything has to be handled appropriately.

So we have our model and our update by now, but we’re still missing one important piece of the puzzle, the view.

There is not really too much to say about our view. We always receive the model and define what should get rendered to the screen. It might be interesting to note that everything is a function here. Our div is a function that expects a list of attributes and a list of child elements, the text function expects a string and so on. So we have built a Counter example and can verify that clicking on the Inc button really increments our model and clicking on the Dec button decrements it.

Checkout out the complete example.

Now that we have a better understanding of how an Elm application is structured, let’s build on the previous example and see how we can subscribe to actions happening outside of our application.

More Basic Examples

Our next example will listen on key presses to highlight a row. By default we will highlight the very first row.

First off all, we will need to create a new file, let’s call it HighlightRow.elm.

Let’s begin by importing everything we need. By now we should know how to define as well as import a module.

We’re importing our well known Html module which exposes div and text functions as well as Html.Attributes, where we will use the style function to style our rows later on. Finally we also need to listen to key presses and get a hold of the keyCode, this is why we need to import Keyboard. So before we continue let’s install Keyboard.

elm-package will ask if it should add the dependency to our elm-package.json and if we approve of the upgrade plan.

elm-package takes care of installing the dependency and updating the elm-package.json.

We’re ready to write our main function.

You will notice that we’re using program not beginnerProgram this time. One difference is that we need to also define subscriptions as well as an init function as opposed to model in our previous example. We will get into more detail in a minute, but before that, let’s think about our model.

If we think it through, we will need a list of rows and a current index. We could have taken a shortcut and simply defined our model as:

But at first glance we can see that by clearly specifying what a Row looks like, we are able to explain nicely how our data structures looks like and we can leverage the fact by defining type annotations like the following f.e.:

Our init function should define, as the name implies, our initial model.

There is something interesting here, compared to our previous examples. We are returning a (Model, Command) tuple not simply a Model. It’s also important to note, that we’re not using any commands in this example, but we will see in depth why they are useful in part 3. For now we don’t really care about any commands and always return Cmd.none.

Initially we’re saying that our index is at position zero and that we have 3 rows to begin with.

A quick recap: another benefit of defining a type alias Row, is that now we can call Row with an integer and a string, which we are effectively doing in our init function when defining the initial rows.

Next, let’s take care of our update function, but first let’s make sure we have a type Msg in place.

Here, we’re simply stating that any KeyPress messages should be handled via our update function and further that we will also receive a keyCode of type Int.

This looks like a lot is happening here. Let’s break this down and go through the above code step by step.

This part should be clear by now, we’re covering any KeyPress messages with a keyCode payload.

What we see here is a let expression, which is useful when we need to declare a variable like result, in this specific case. Again, we handle any possible case code might have and assign a value to result. the _ -> means handle any other case, due to the fact that we need to make sure that any other keyCode aside from 97 (a) and 115 (s) is also considered otherwise our example will not compile.

Finally we’re assigning the result to our currentIndex and returning the updated model. Again, we’re returning a (Model, Command) tuple here, just like in our init function.

Our view code is composed of a viewRows and a viewRow function which take care of displaying the rows and highlighting the currently selected row.

Our main view function calls viewRows with the model.

Here, we’re iterating over our rows and calling the getRow function, which in itself is the partially applied viewRow. Again, we’re using let ... in to define the getRow. Creating the rows is done by mapping via List.map and calling getRow for every single item.

Our viewRow finally checks if the row’s position is equal to the selectedIndex and if so, adds a style definition (“color”, “red”) to the basic styling. We can also see how basic styling can be applied in Elm. The style function expects a List (String, String) containing the property and value. This approach should be only used lightly. There are more efficient ways to style an Application in Elm as we will see in a later part of the tutorial.

All that is left to do, is to define our subscription function.

With subscriptions we have the capability to listen to inputs that are outside our application, f.e. mouse events or key presses. To get our function to work we will need to subscribe to all key presses via presses.

There are situations where might need to listen to more than one input.

By using Sub.batch we can pass in a list of subscriptions and get a subscription in return that takes care of listing to all the provided subscriptions.

Checkout the complete example.

Summary and Outlook

The second introductory part was mainly focused on introducing the Elm Architecture (TEA) and should have covered the basics. You should be able to write small modules, understand what subscriptions are and how to dynamically render a list of items. We still need to cover more basics before we can start building our real world example. Part 3 will focus on side-effects handling via Commands, JavaScript interop and more fundamental topics.

Part 1: Part 1: Getting Started For The Absolute Beginner

Part 3: Effects, JavaScript Intertop and more fundamentals.

Incase you have any questions, please leave feedback on Twitter.

--

--

A. Sharif
JavaScript Inside

Focusing on quality. Software Development. Product Management.