Introduction Into Elm

Part 1: Getting Started For The Absolute Beginner

A. Sharif
JavaScript Inside
Published in
12 min readFeb 26, 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. While this part, “Getting started”, will mainly focus on setting up Elm and building small examples, including playing around with the REPL, part two will already include more advanced topics like side-effects and the Elm Architecture (TEA).

Once we have laid the foundation in the first two parts, we will start building a real world example and dive deeper into the already learned as well as look into topics like scaling the basic examples and testing. I’m planning to break this series into 6 parts, but might vary depending on feedback.

Thinking in Elm

The fundamental principle of writing Elm code is to separate effects from computation, and to use different techniques for each.

All computation is “pure”, meaning what a function returns depends only on its arguments. It’s predictable and easy to understand.

Max Goldstein, What does it mean to use Elm?

We do not think in terms of reusable components. Instead, we focus on reusable functions. It is a functional language after all!

Scaling The Elm Architecture

Installation

Before we can start to write any Elm code, we will need to install the platform first. Visit https://guide.elm-lang.org/install.html for a detailed explanation on how to install Elm for your setup.

If you want to tryout Elm prior to installing it — you can visit the online editor available at http://elm-lang.org/try (recommended for starters) or as an alternative try the more advanced editor at https://ellie-app.com.

To follow along with the examples though, you will need the REPL, so installing Elm is recommended if you want to run the examples.

By the time of this writing, Elm 0.18 is the current version. This might change in the future. I will try to update this tutorial by explicitly declaring any changes (including the date).

Basic Example

We can verify if Elm has been installed by typing elm --version and should see: 0.18.0. Once the installation has been successful we gain access to a number of CLI tools: elm-repl, elm-reactor, elm-make and elm-package. We will get into more detail regarding the aforementioned tools, for now we’re interested in elm-package, which takes care of downloading the needed elm packages.

First of all let’s create a new directory and name it elm-introduction or you can choose to give it any name you like. Once we have created this new folder, we will run our first command in said folder.

The prompt asks us if we want to install the following packages.

After approving the plan, elm-packages will install the proposed packages in the newly created folder elm-stuff, where the packages are stored. Besides handling the installation, you will also notice a newly created elm-package.json file which lists the projects dependencies and information like version, license and summary (think package.json in a JavaScript context).

We’re ready to write our very first basic example. Create a file called Basic.elm and add the following code:

We will get into more detail regarding what we’ve written here in a minute. Just to see the example render the string “Introduction into Elm” we will need to run the following command on the command line: elm reactor

This will start a server at http://localhost:8000 by default.

Navigate to the Url and click on Basic.elm in the File Navigation list. You should see the text “Introduction Into Elm” printed on the screen.

Excellent, we have the most basic of all examples up and running and while this sounds boring, we can already take away some interesting facts. So let’s go through the example.

Every module in Elm is defined by the module keyword followed by the module name which is also reflected in the file name (Basic.elm incase of the Basic module). Finally exposing defines which types and functions are available to any modules importing Basic. In this specific case (..) means we are simply exposing everything.

On the next line we are importing the previously installed html package and explicitly defining what functions or types we want to have imported. Incase of Html, we want Html and text. We will also see cases where we only import a Module without defining what we explicitly want exposed in later examples.

To render something to the screen we will need to define a main function, which returns the Html needed for displaying anything useful. In our basic example we pass the string “Introduction Into Elm” to the text function.

With this knowledge in hand let’s continue to discover what Elm has to offer.

Functions

To get a better understanding of what we can do with Elm, besides rendering a string onto the screen, let’s focus on the most basic building blocks. To follow along with the examples let’s start the REPL. Type elm-repl on the command line.

We should be greeted with the following message once we’re inside the REPL.

We are ready to write a couple of functions now.

Let’s take the following JavaScript code for example.

The equivalent in Elm would be:

We declare any function arguments after the function name separated by whitespace, there are no parenthesis or commas as opposed to the JavaScript example.

Let’s run multiply inside the REPL.

We get a type definition of the specified function in return. Let’s reiterate on the previous example and add the type signatures beforehand.

We’re declaring a function signature for multiply here.

Short excursion: Types

Our multiply function expects a float and another float as arguments and returns a float at the end.

One interesting fact is that we’re not distinguishing between the input and the return types. You might be thinking (Float, Float) → Float would be a valid approach, but consider the fact that we might be partially applying or currying our function. What if we call our multiply with only the first argument, we would get a new function back expecting a float. So taking this fact into account, type annotating our multiply with Float → Float → Float makes sense. And although type annotations are optional in Elm due to type interference it’s still highly recommended to type annotate all our functions.

Take another example where we might not know if we have a string or an integer when iterating over a list of elements, but we want to be able to write a function that concatenates all elements and returns a string. How would be able to achieve this?

Defining concatAnything = List String → String would mean we can only handle a list containing strings but not any numbers or any other types for that matter. Now instead of defining a specific type we can choose to resort to using type variables instead. Going back to the concatAnything function, that would mean we can define the type as List a → String. a can be any other string like foo or bar, but a, b etc are more common and the preferred way.

So we know that our multiply function expects a float and another float as arguments and returns a float at the end now.

Let’s see what we can do with our multiply.

The above behaves as expected, but we can also only pass in a single argument:

Calling multiply with 4.5 returns a new function expecting a single float argument. This means we can partially apply our functions in Elm, which makes it possible to pass around functions with predefined values. Let’s see how this works:

Elm will always return a new function as long as not all arguments have been provided. We will leverage this in our real world examples. Let’s also take a look at composition in a case where the result of one function is needed as input for another function. Maybe we want to multiply by 3 and then add 5. Let’s see how we can solve this in Elm.

Just incase you’re wondering what the —- means, it’s a single line comment, while {- -} is used for multiline comments.

The above example already works, but we can take a cleaner approach then grouping the function calls with parenthesis. Instead we have other options at our hands. Let’s see the same example as above but this time using the forward and backward function application for better readability.

The forward application uses the |> pipe operator.

The backward application uses the <| pipe operator.

What we have seen up until now have been named functions, but we can also work with anonymous functions. Again, we’ll build a multiply function that expects two float arguments and returns a float as a result.

I think we should have a basic understanding of functions by now, but we’ll dive back into the topic as soon as we start building our real world application and see what else we can do. From an introductory perspective this should suffice.

Data Structures

Elm offers a number of data structures including List, Array Dict, Set, and Tuples.

Our focus will be on Records and Lists, as this is what we will encounter straight away when working with Elm.

Other structures like Tuples or Set will be handled when we need them, once we start building our application. To keep things focused we will ignore them for now.

Records

Think of records as the equivalent of objects in a JavaScript context, with the difference that all fields are defined and that you can only ask for fields that really exist. Let’s take a look at a record.

It’s also advisable to create a type alias that describes the User record, because we will probably create more than one User.

Now we can use the User alias as type signature as well as a constructor. This means we can create users by passing the needed arguments to User.

Accessing a field can be achieved via the dot notation.

Alternatively we can use accessor functions that are named after the field name, .id for field id, .location for location and so on so forth.

Finally an example showing how we can update a record field. Updating any field or fields returns a new Record obviously.

There is more we can do with Records, check the docs for further information and examples.

List

Creating a list can be achieved by using [].

Elm interferes with the types and tells us that we have a List containing strings. It’s important to note here that all elements have to be of the same type.

So we can already see it doesn’t matter what type the elements have, they only have to be of the same type. For example mixing types will lead the Elm compiler to return the following error message.

So we can clearly see what the problem is — via the error message. Also, this is a good example for how nice error messages are in Elm. They clearly indicate what went wrong and how to fix it. This is just one of the many benefits that come with using the language.

List offers a large number of functions to manipulate lists. Let’s go through a couple of them just to get a better of feel of what’s possible, while the official docs offer a more complete and detailed overview.

List offers more functions like drop, reverse, head and tail f.e., furthermore we have access to functions like map and filter.

So we see that map can work with anonymous functions but we can also use our previously defined add5 function to increment every element by 5.

The list module also exposes map2, map3, map4 and map5 for functions that expect 2 or more arguments. Let’s also take a look at filter, which again works with named as well as anonymous functions.

To round up the List section, let’s finally see how we can reduce list values. We have foldr and foldl to our disposable. While foldr reduces a list from the right, foldl does the same from the left.

Summary and Outlook

This introduction part was mainly focused on introducing Elm from an absolute beginner perspective and should have covered the most basic topics. You should be able to print a plain text to the screen, know how to use the REPL as well as know how to start an Elm module. Furthermore we should have covered functions and data structures as basic building blocks for the upcoming chapters.

Part 2: The Elm Architecture (TEA)

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

--

--

A. Sharif
JavaScript Inside

Focusing on quality. Software Development. Product Management.