Getting Started with Elm v0.17

Last Updated On 05/13/2016

The Elm Tangram

Introduction

Elm is a really neat functional language that has a fantastic (and super fast!) virtual DOM implementation that makes it possible to write full functional code instead of JavaScript for our web applications. It can do neat things like HTML5 games, 2d/3d demos, or just replace things like react.js/jquery/etc for building webpages. If you want to learn a bit more, visit http://elm-lang.org/ for examples. We’re going to walk through a quick introduction to Elm and how to write Elm code, eventually building up to more and more complicated components and hooking it into a Phoenix backend.

Instead of starting off going over all of the bits and bobs of the Elm language, we’re going to take a more practical approach and learn by doing! We’ll start writing components and explaining what we’re doing as we go along so that we can be prepared to write a major application in Elm! Also, given my posting history, suffice to say that we will be relying on Elixir/Phoenix to provide the backbone for our application that we write (when we get to that point).

Current Versions

Elm: v0.17

Understanding Modules and Imports

Let’s start by taking a look at a minimal Elm application, and we’ll explain it piece by piece:

module Greeting exposing (..)
import Html exposing (text, Html)
main : Html a
main =
text "Hello World"

Every Elm component you write should start off with a very simple line at the top. This exposes the name of the module that you’re writing and helps dictate how to reuse your own component or how others can utilize the work you’ve already done. Given that, let’s start off with our module declaration:

module Greeting exposing (..)

This is our module declaration; it tells Elm what to use when it is referencing this module in the future. Next, every Elm component imports whatever is required to make your app work. We’re going to start very, very simple and just stick to what we’re actually going to use for the start. import statements in Elm have two parts (one of which is optional): which package to import, and what parts of that package to import into the namespace of the current module. Let’s take a look at a few examples (and what the differences mean):

import Html

This imports an Html module into your current module, so you can call functions/references to the Html module via Html.whatever. We’ll be using the “text” function in our first component, so we could now call that function via:

Html.text “hello world”

We may be writing this a lot, and the hallmark of any good developer is knowing when to be lazy, so let’s explore a few other options.

import Html exposing (text, h1, div)

This tells Elm “I want you to give me the text, h1, and div functions from the Html module” Now, our example above becomes:

text “hello world”

Another example:

import Html exposing (..)

Our final example here says “You know what? Just give me everything inside of the Html module and let me access it without specifying what module it came from.” This will expose text, h1, div, and whatever else Html provides.

Now, before we get too far ahead, we casually dropped a reference to calling a function in Elm, but haven’t talked about the syntax.

Functions in Elm

So, we said that Html.text is a function, but we didn’t talk about why we wrote our invocation the way we did. Let’s take a step back and talk about that first.

For the next example, I’m going to use something called the Elm REPL. A REPL is a read–eval–print loop; it’s basically an interactive terminal that allows us to run/evaluate some Elm code and see what the output is. If you want to test out some Elm in a REPL yourself, you’ll need to follow the install instructions from the Elm website. From there, create a new directory to act as your work directory. In that directory, run the following command to install the packages you’ll need to follow along:

$ elm package install elm-lang/html

And answer yes to the questions about if the install plans look good. Now, you can start up the repl with the elm repl command. This is what it looked like on my machine:

$ mkdir elm-intro
$ cd elm-intro
$ elm package install elm-lang/html

And the output:

To install elm-lang/html I would like to add the following
dependency to elm-package.json:
    "elm-lang/html": "1.0.0 <= v < 2.0.0"
May I add that to elm-package.json for you? (y/n) y
Some new packages are needed. Here is the upgrade plan.
  Install:
elm-lang/core 4.0.0
elm-lang/html 1.0.0
elm-lang/virtual-dom 1.0.0
Do you approve of this plan? (y/n) y
Downloading elm-lang/core
Downloading elm-lang/html
Downloading elm-lang/virtual-dom
Packages configured successfully!

And now to bring up the Elm REPL, we’ll run:

$ elm repl
---- elm repl 0.17.0 -----------------------------------------------------------
:help for help, :exit to exit, more at <https://github.com/elm-lang/elm-repl>
--------------------------------------------------------------------------------
>

If we attempt to call Html.text in an Elm REPL (after importing it, of course) with no arguments, we’ll get the following output from Elm:

> import Html
> Html.text
<function: text> : String -> Html.Html a

Elm has a GREAT type declaration system and it allows functions to be very explicit in how they’re used. From the example above, we see that Html.text is:

Defined as a function called “text” in the Html module Takes in a single String as the first argument Returns a result in the form of the Html.Html a type (this just means it returns a special type defined in the Html module that we can just think of as being part of the virtual DOM) and that it doesn’t just return the Html example, but it also includes a variable type (defined by a). This will typically be your Message type.

I’ve had a few people that were working with Elm express a little confusion about the actual type declarations for an Elm function. There are a couple of important components. We’ll start by looking at a sample type spec and pulling it apart:

view : String -> Html a

The first bit (view) tells us the name of the function

view : String -> Html a

The colon tells us we’re now looking at the argument/return declaration The last element in the list is ALWAYS what is returned. In this case above, we’re returning something of type Html a.

view : String -> Html a

Every other element in the list are ARGUMENTS. In the above example, we have a single argument, which is of type String.

view : String -> Html a

If the type is something like String or Integer, it has to accept/return a specific TYPE.

If the type is something like “a” or “b” it means it’s a variable type.

Arguments are passed to functions in the format of function_name arg1 arg2 arg3… Note the lack of commas! Arguments are separated by spaces.

Putting everything together, we can see that:

text "Hello World"

Is us calling a function that we imported called text and passing it a single argument of “Hello World”. Given this, we’re going to make sure we do our type specifications for everything we write going forward.

Continuing with Building our Component

Next, we need to define a main endpoint that will render out everything we need on the page, so we’ll define a main function for that explicit purpose.

main : Html a
main =
text "Hello World"

Which should give us a page that says “Hello World”! What we actually did here is define a function that itself calls a function (imported from the Html library) called “text”, and passed a single argument to that function (a string containing “Hello World”). In addition, we defined a type specification for our function that tells the following: the name of the function is “main”, and the type that our function returns is “Html”.

Remember my earlier note about how the last element in a type specification is what is returned. The text function returns a single Html node, so that is what we’re going to tell Elm that our function returns. Our final code at this stage should look like this:

module Greeting exposing (..)
import Html exposing (text, Html)
main : Html a
main =
text "Hello World"

And our end result:

Our updated Hello World app!

Running an Elm Application

If you’re following along and new to Elm, you may be a little confused about how precisely I got my Elm application from code to browser! One other nice utility that Elm provides, similar to the Elm REPL, is something called “elm reactor”. Elm reactor is a little website that allows you to run Elm code inside of your browser without having to go through the various make steps to build an index/js file.

So, if we have a directory containing our elm file (I called it “fig1.elm”), we can run (in that same directory):

$ elm-reactor

We should see some output on the page:

elm reactor 0.17.0
Listening on http://localhost:8000/

And if we visit http://localhost:8000/ we should see any elm files in that directory listed out. Click on the elm file you’re working with and you’ll either see the application running just fine (just like the above) OR you’ll see the Elm error messages if your code did not compile! Now we can keep that window open and refresh when we make any changes to see how it changes our Elm application in a simple way!

Writing a View function

Let’s write our own function that will function as our main view. We’ll modify the main function to then call that new function we’re writing. Above main, add the following:

view : Html a
view =
text "Hello World"
main : Html a
main =
view

Remember that functions in Elm are defined as:

functionName argument1 argument2 (...etc) =
functionBody

All we’ve done here is move the work of the display in main to a helper function. That helper function currently takes no arguments and just returns an Html element, so the type specification for that is simple. In addition, since main just calls that function, its type specification should be the same.

What if we want to pass the message along directly to be displayed as a Text node? Simple, we’ll give our view function an argument!

view : String -> Html a
view person =
text ("Hello " ++ person)
main : Html a
main =
view "World"

We’re doing a few new things here, so let’s explain a little bit. First off, we’ve modified our view function to take in an argument, so we need to update our type specification to match. Remember that type specifications are in the form of name : Arg1 -> Arg2 -> …ArgN -> ReturnType, so here we are specifying that the view function takes in a single String type and returns out an Html node. (String is one of the built-in types for Elm, just like Integer, Float, and a few others).

Second, notice the new parentheses. text only takes a single argument, so if we want to call something that itself uses a function, we need to wrap that inside of parentheses. Remember that Elm uses spaces as separators for arguments, so if we wrote something like this:

text "Hello " ++ person

It would assume that we were attempting to send three arguments to the text function: “Hello”, ++, and person. We had to wrap our argument to the text function inside of parentheses because ++ itself is a function, and we want the result of that append function sent to our text function.

Defining a Model for our Component

It’s not enough for us to just define a view function and call it a day. What if we need some concept of state to add to our function? We would handle this via a model that would be used by the component!

We’ll start off simple and build big:

model : String
model =
"World"

And we’ll change our call to view inside of our main function to instead be passed the model.

main : Html a
main =
view model

Our application itself still hasn’t changed but we’re in a better place for organizing our code. It’s now much simpler to add functionality to change our message, for example. Let’s take a look at our codebase so far:

module Greeting exposing (..)
import Html exposing (text, Html)
-- MAIN
main : Html a
main =
view model
-- MODEL
model : String
model =
"World"
-- VIEW
view : String -> Html a
view person =
text ("Hello " ++ person)

So, we did one more new thing here. Any text coming after double-dashes is just a comment.

We now have a working concept of state and a component that we can build and reuse wherever we need to. Now we’re going to start modifying this component out a little more to be a little more complicated and store a to do list for a person. We’ll start by allowing a person to modify their displayed name, and to do that, we’ll need some more complex html!

Building Our Name Changer

We’ll need to modify two parts before we get started. First, our import Html statement needs to be expanded to include more functions. We’re going to opt for laziness here and change that line to:

import Html exposing (..)

This will give us access to calls like div, input, span, etc. Next, let’s tackle our view function we defined earlier:

view : String -> Html a
view person =
text ("Hello " ++ person)

Let’s make this display a little better. We’re going to use the h2 tag as well as a text input and a button.

-- VIEW
view : String -> Html a
view person =
div []
[ h2 [] [ text ("Todo List For: " ++ person) ]
, input [] []
, button [] [ text "Add Item" ]
]

Here, we’re defining a div as our root element that we’ll be displaying out. Every HTML element in Elm takes in two arguments: a list of properties and a list of sub elements. The text function we’ve been using creates a single element (text, unsurprisingly). It’s a good start, but it’d be nice if we could start using CSS classes and html attributes to set things like placeholders, too. The good news: we can do precisely that! Let’s go back to our import statements at the top and add a new import:

import Html.Attributes exposing (..)

Now we’ll head back down to our view function and change the input function call to pass in some properties. Remember that the properties argument (the first argument) is a list of properties. A list is exactly what it sounds like: a list that can contain multiple elements. Let’s look at the new function:

, input
[type' "text"
, placeholder "New Todo Item"
] []

Note the little gotcha on the type property: we have to specify it as type’ (note the apostrophe), since type is a reserved word in Elm. Other than that, if you know your Html, we’re specifying the type of the input and the placeholder text. It’d be the equivalent of us writing:

<input type="text" placeholder="New Todo Item">

Let’s also go back and set up an unordered list to hold our elements. We’ll also give it a sample list item so we can get a feel for what everything will look like. If we revisit our view function, we can modify it to the following:

-- VIEW
view : String -> Html a
view person =
div []
[ h2 [] [ text ("Todo List For: " ++ person) ]
, ul []
[ li [] [ text "Sample Todo Item"]
]
, input
[type' "text"
, placeholder "New Todo Item"
] []
, button [] [ text "Add Item" ]
]

Our finished product:

Our fancier UI for the todo list app we’re building

Next Steps

This is all pretty cool, but we need to start expanding this a bit more by adding responsive functionality. We want to be able to modify the name of the person and talk about how to change the state of our application over time. In the next post we’ll start talking in greater depth about some of the more powerful (and more complicated) parts of Elm such as Subscriptions.

In addition, we’ll be hooking our Elm application into a Phoenix application later. If you’ve never messed around with Elixir or Phoenix, I highly recommend it. I’ve written a bunch of tutorials on the subject, so if you want to get started I recommend starting at the beginning:

The code for this is available as a Gist at:

Our finished first Elm application
Show your support

Clapping shows how much you appreciated Brandon Richey’s story.