Elm Web Development

4 (3 reviews total)
By Ajdin Imsirovic
    Advance your knowledge in tech with a Packt subscription

  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Why is This a Great Time to Learn Elm?

About this book

Web development with JavaScript usually involves dealing with performance and maintenance issues. JavaScript fatigue makes it difficult for many developers to keep up with the increasing complexity posed by the multitude of JavaScript frameworks with changing versions, and the need to use different tools such as task runners, module bundlers, compilers, testing suites, linting and debuggers. Elm is an easy-to-learn, functional programming language that simplifies web development by eliminating the complexity associated with using JavaScript for web development. Elm is a statically typed language and thus makes a front end web developer's life easier by preventing run-time errors.

You will begin by seeing the bigger picture of where Elm fits into the web development world and learning the basics of Elm programming. Firstly, you will get a taste for web development with Elm by developing a simple fizz-buzz app. Next you will get hands-on with advanced Elm concepts as you develop your own personal blogging website, a unit conversion app and a weather app with Elm. Finally, you will also learn how to integrate Elm with a Rails 5 app with the help of Webpack. By the end of the book you will have learned Elm programming, and its applications, and will appreciate how Elm simplifies web development for you.

Publication date:
March 2018
Publisher
Packt
Pages
288
ISBN
9781788299053

 

Chapter 1. Why is This a Great Time to Learn Elm?

Welcome to the first chapter. This chapter is a gentle introduction to the Elm language. Our goal for this chapter is to understand the following:

  • What is Elm?
  • What is unique about Elm in the saturated field of frontend web development
  • How does Elm compare to JavaScript?
  • How to get started fast with the help of Ellie-app, Cloud9, and the Atom editor

After completing this chapter, you will be able to:

  • Compare the Elm code with the JavaScript code at a basic level
  • Set up and use Elm in several different ways
 

What is Elm?


Elm is a functional programming language aimed at frontend web development. In the words of Evan Czaplicki, the inventor of Elm:

"I wanted to do front-end stuff, but I wanted ... front-end programming to feel really, really pleasant." 

Evan is a functional programmer, and he wanted to apply, in practice, some of the academic concepts from functional programming, with the goal to make the whole experience of frontend web development a nicer one.

One important issue with the current way that frontend development works is the problem of accidental introduction and aggregation of small errors during development. While developing frontend JavaScript, errors will appear in our code, and very often, they will go unnoticed.

At a later point in time, these errors will result in our code breaking. That leads to a lot of time wasted on fixing the issues that we, developers, unintentionally brought to our code, through a number of obscure errors.

Elm makes it virtually impossible to introduce errors into our code. Through a combination of non-cryptic compile-time errors and a helpful type system, introducing errors into our code is on the verge of being impossible.

If we needed to point out just one thing that is awesome about Elm, it is this lack of runtime errors. Imagine working in JavaScript and never seeing runtime errors, and you might begin to appreciate the time savings that can be achieved by using Elm.

After you have worked with Elm for some time, you start to feel as if Elm is constantly narrowing the window of opportunity for errors to appear in your code, from the way it is structured to the way you work with it. You can feel that conscious effort was placed on making it less probable for errors to occur.

For example, as Elm is built around the paradigm of functional programming, it works with pure functions. Pure functions are functions that have no side effects. In other words, pure functions have no state. They accept parameters, and they return a value. That is all they do!

They will not go out and make an HTTP request. They will not mutate variables. They will not, in any way, change the state of the world. They simply return a value. This brings us to an interesting effect—as long as we provide the same values to a function in Elm, it will return the same result.

Another wonderful benefit of pure functions in Elm is that you can be certain that all the changes you make in your code are local. Changing a piece of code in your app will not cause some other piece of code in your application to stop working. 

To be fair, it is possible to write pure JavaScript applications in this restrictive functional style. However, there is nothing built-in in the language itself to throw an error if we lose our focus and start writing impure functions. Also, when working with third-party libraries, you cannot be sure that they have adhered to functional style purity. Compare that to Elm, which enforces nothing else but pure functions.

There is one caveat to this talk of pure functions. You are probably aware that a fully stateless application would be pointless. The ingenuity of Elm lies in the fact that it has a very strict way of dealing with updates to our application. The takeaway from this is that not only does Elm enforce the functional programming paradigm by forcing us to use pure functions, but it also narrows down ways to deal with the outside world.

The immutability of data that Elm has naturally leads to another great benefit: debugging. Since you can be certain that a random piece of your code will never be able to affect another piece of your code, it will simply not be possible to have to deal with the issue of not knowing which part of your code broke the application; that is, which part of your code is causing the error. In JavaScript, this is a common issue, which is literally nonexisting in Elm.

Another feature of Elm is that it works in a way that allows us to cater for events happening in our app, while still maintaining immutability. Elm is also a great way to start learning functional programming without having to understand a lot of advanced concepts (which would be the case when trying to learn other, more difficult functional programming languages).

Why is it worth learning Elm?

In this section, we will discuss some of the reasons why Elm is such an exciting language to learn.

These reasons are as follows:

  • Blazing-fast virtual DOM
  • Friendly compile-time errors
  • Zero runtime exceptions
  • Constraints as guarantees
  • Piped syntax
  • Easy refactoring
  • Increased productivity
  • Helpful type system
  • Time-traveling debugger

Now that we have an overview of the exciting concepts that Elm brings to the table, let's inspect each of them in a bit more depth.

Blazing-fast virtual DOM

In order to be able to discuss the virtual Document Object Model (DOM), we need to first understand what the DOM itself is. It is a standard that is proposed and maintained by the the World Wide Web Consortium (W3C). 

As defined by W3C on their website:

"The Document Object Model is a platform-and language-neutral interface that will allow programs and scripts to dynamically access and update the content, structure and style of documents. The document can be further processed and the results of that processing can be incorporated back into the presented page."

As per the W3C's DOM Specification Level 3 Core, the DOM is a way to access and manipulate documents in a tree-like structure consisting of nodes.  We can summarize the DOM as having the following characteristics:

  • The DOM is language-agnostic, meaning that in theory, any language could be used to manipulate it
  • The DOM is set up in such a way so as to allow for live manipulation of itself
  • The DOM consists of a set of objects (a nested, tree-like hierarchy of objects) 
  • The DOM also has methods to manipulate this hierarchical structure; in other words, the DOM is an Application Programming Interface (API)

Before we continue the discussion about the DOM, it is very important to make it perfectly clear just what an API is. To explain what the API abbreviation stands for, we can use the analogy of switching a light bulb on and off. In order to switch on a light bulb, we do not have to know what material it is made of, how much power is needed to run it, or how bright it is. All that we need to know is how to make it light up the room, and how to turn it off; that is, how to control its behavior.

Of course, as we are all very well aware, to control a light bulb we need to use a light switch. In the preceding example, we could say that the light switch is the API for the light bulb. In order to manipulate the light bulb (to make it do what we want), we need to access it via its API (the light switch).

In more general terms, we can think of an API as a way to access and manipulate, or control, in a structured manner, whatever it is that we want to work with.

In the case of the DOM, its purpose is two-fold. First, the DOM is the API for HTML. Using the light bulb analogy, HTML would be the light bulb, the DOM would be the light switch, and JavaScript would be pretty much the only way to access the light switch.

Secondly, the DOM is a representation of a web page. The previous sentence is crucial for our understanding of how the DOM works, so we will repeat it again.

The DOM is a representation of a web page. This representation is a tree-like structure that consists of building blocks—node objects. In total, there are 12 types of nodes in the DOM. However, this representation of a web page is not static, meaning it is not only a static representation of a hierarchical structure of a web page. Each node also includes all the properties and methods that enable us to alter the contents of this tree-like structure (the API part).

Practically, there is only one way to access the DOM—via JavaScript. There are some exceptions to this. For example, in IE 10 and lower, we could use VBScript, but for all practical purposes, it is safe to say that the only language that can work directly with the DOM is JavaScript.

In order to understand the interplay between web pages, HTML, the DOM, JavaScript, and browsers better, we need to understand what happens when you point your web browser to a web page. What follows is a very simplified explanation.

First, the browser will make a call to the server, which will return some HTML. The browser's rendering engine will then parse the raw HTML into its DOM representation. Parsing is the process of translating from one format to another. The raw HTML that is served from a web page cannot be directly manipulated. Instead of directly manipulating the raw HTML, we must use the DOM API, and therefore, the HTML of a web page needs to be internally parsed by the browser's rendering engine into its DOM representation.

Once the browser's rendering engine has parsed the HTML document into its object representation (the DOM), it is now available to JavaScript, which can manipulate the DOM representation of the HTML document.

Another way of looking at the relationship between HTML, the DOM, and the browser, is to understand that HTML is simply just text. It becomes what we see on our monitors only after the browser has parsed it into its DOM representation.

Several years ago, browsers were not complying to standards set forth by W3C. In recent years, they caught up to it, and the DOM API in all major browsers is, for the most part, standards-compliant. However, there are issues with the DOM.

HTML and CSS were not intended for modern web applications, with thousands of nodes that sometimes have to be updated according to certain criteria. Still, today, this sort of behavior on modern web pages is what users expect. Single-page Applications (SPAs) are a good example of this. In SPAs, the DOM needs to be constantly updated, and yet, the way that the DOM works makes its direct manipulation costly.

Several years ago, we would be using jQuery as the de facto standard of dealing with interactivity in our web pages. Thus, in case we'd want to update the page based on, say, a click event, we would tell jQuery to first find all the nodes in the page that need to react to that click event. Then, we would update the nodes.

Thus, the jQuery paradigm is that we are working with the DOM directly. For example, to target all the div elements on-click, and have them perform some kind of a change, we have to first set up our div selector, and then give it the event handler and the action to perform, like this:

$("div").on("click", performAction())

The issue with this is that there will probably be at least half a dozen events to listen for in your web app. There will also probably be a lot of DOM nodes that will have to be updated.

Direct DOM changes are slow. The larger your app gets, the more resource demanding direct DOM manipulation becomes, as there are more layout recomputations and reflows to be done when the DOM changes. Piling up these changes and web page reflows might lead to pages not being as snappy as users would expect.

Another issue is that as our web app grows, it gets harder to avoid bugs, because when working with the DOM directly, it is hard to separate concerns. The idea of virtual DOM is amazing in its simplicity. Instead of constantly touching the DOM, a much better approach is to have the whole DOM structure represented virtually, then keep a snapshot of the DOM, represented as virtual DOM nodes.

When the page needs to be re-rendered as a result of an event, the previous DOM snapshot is compared to the new DOM structure, and then only those changes that are needed to achieve the end result are performed on the actual DOM. 

To explain the distinction between the direct DOM manipulation and the virtual DOM better, let's look at two programming paradigms: imperative programming and declarative programming.

In imperative programming, we explain the how to get to the what. In other words, we need to give our program a full set of detailed instructions on exact steps that need to be performed in order to get to the desired result.

In declarative programming, we simply ask for the what to be performed; the inner workings of how the program should get to the desired result is not a concern. An example of a declarative language is SQL. For example, we could say:

SELECT * FROM Customers
 WHERE Car='Mercedes' AND Color='Green';

We do not give SQL instructions on how to perform the action; we just tell it what result we want. In other words, we do not specify all the steps that SQL needs to take to search the database for customers that own a green Mercedes. We simply tell it what we want, but the implementation (how SQL does it behind the scenes) is not something that we have to know.

This is very similar to how virtual DOM works in Elm. We specify the result of our change, and let the Elm Runtime decide on the most effective way to get there. 

To wrap this section up, it is interesting to note that Elm has the fastest virtual DOM implementation of all the major JavaScript frameworks, which is quite an accomplishment.

Friendly compile-time errors

It is kind of interesting how there is an entire movement revolving around ideas like UX and UI. We web artisans strive to give our users the best possible experience while interacting with our websites and apps. But, besides being the construction workers of the web, ultimately, we are users too.

Before we look at an example of a JavaScript message, let's remember the only reason we are using JavaScript in these examples is because, as we discussed earlier in this chapter, it is practically the only language that works with the DOM API. Looking at some JavaScript error messages, it is interesting that we are still stuck with cryptic errors such as:

Uncaught TypeError: undefined is not a function

Why such a terse error message? Definitely not beginner-friendly.

Let's break this error message down, starting with Uncaught. Obviously, our application failed to catch this particular error. Next, TypeError. It is just one of several kinds of errors. Others include SyntaxErrorURIErrorRangeError, and so on. 

Finally, the message—undefined is not a function. It helps to know that undefined is one of JavaScript's primitive types. Others are string, number, null, boolean, and symbol. When a variable has been declared, but has not been assigned a value, the JavaScript engine assigns it the value of undefined. The undefined value is the only possible value of the undefined type. Thus, undefined can never be a function.

In other words, your code was trying to call a value as if that value was a function. Obviously, you can't call a non-function, hence the not a function part of the error message. Armed with this knowledge, let's rephrase our error to something that is a bit more user-friendly.

How about this:

Type Mismatch: Your code was trying to run a function call on the primitive type of undefined. That is not possible, since only functions can be called, and undefined is not a function.

Isn't that nicer? It helps us understand the error much better. But what if the compiler took it a step further? What if it not only told us how the error occurred, but also the most probable culprit in our code?

That's what Elm is aiming at. What follows is a trimmed-down example of an error message straight from Elm's documentation:

-- TYPE MISMATCH -------------------------------------- types/list.elm
The 3rd element of this list is an unexpected type of value.
15| [ alice, bob, "/users/chuck/pic" ]
                  ^^^^^^^^^^^^^^^^^^
All elements should be the same type of value so that we can iterate over the list without running into unexpected values.

As I infer the type of values flowing through your program, I see a conflict between these two types:
  HTML
  String

As I infer feels like we have a friend in there somewhere, and he's/she's actively trying to help us. Another awesome thing about Elm's errors is that as new versions of the language come out, we can see efforts being made to continuously improve the friendliness and usefulness of compiler errors. This idea was nicely summed up on Elm's official website in one sentence:

"Compilers should be assistants, not adversaries."

For example, in Elm 0.15.1, error messages were improved so that the errors list the line numbers and the actual code, just as we've seen in the previous example. Plus, they highlight the exact part of the code that caused the error (using caret symbols).

Another improvement was to introduce helpful hints to error messages. Thus, we got error messages with pinpoint precision, and the hints and the contexts of these messages helped reduce the time a developer would need to spend getting to the root cause of the error and the most likely way to solve it. Instead, compiler does all this work for us.

In version 0.16, Elm introduced type diffs, which takes an idea from Git. However, instead of comparing commits, the compiler compares types. It also introduced helpful beginner hints, better error messages, and a number of other improvements, which reinforces the idea that Elm's compile-time errors are only going to get better as new versions of the language come out.

Zero runtime exceptions

Elm's programming paradigm is restrictive in comparison with JavaScript's. It forces us to think and work in a way that is a lot more robust and less error-prone. At compile time, Elm will catch errors and suggest, with helpful messages, how to correct those errors. It nips in the bud the billion-dollar mistake of JavaScript by making sure we don't have values that are null.

If we do in fact get errors in our program, it simply won't compile. All of this has lead to esteemed members of the Elm community to make bold claims of zero runtime exceptions. In my experience so far with Elm, this has certainly been true. 

Errors are still possible. Logical errors, that is. Logical errors occur when we developers write code that works, but is nonsensical. For example, having a user who is a negative number of years old. The important thing with zero runtime exceptions is that the application will always run smoothly once it compiles. There will not be a situation where it crashes while running.

However, even if it turns out at a certain point in the future that it is possible after all, for some strange reason, to very rarely cause runtime exceptions in Elm, I would be happy with that too, because that would still be way better than what frontend web developers have to deal with day-to-day, outside of Elm.

Constraints as guarantees

The simplest explanation for the functional programming approach is—functional programming is a programming style in which functions have no side effects.

Ultimately, this means that it's harder to start working with Elm if you're experienced working with JavaScript. Why? Simply because JavaScript will not force you to adhere to certain conventions, or to certain constraints. Thus, the number of ways to do things in JavaScript is greater, and it can feel awkward not having that freedom of choice when you first start working with Elm.

On the flip side, once things break, JavaScript's freedom of choice that initially felt like a good thing, now comes back at us, in the form of one simple question—what dependency broke my code? Very often, searching for this dependency in JavaScript can be a frustrating experience.

Thus, having constraints in place from the get-go might feel a bit strange when you first start working with Elm, but you'll sure be glad you had to work with them when your future self comes back to revisit the code.

When you first start working with Elm, its functional programming paradigm feels like a constraint, but it also acts as a guarantee of things working only in a certain way. A limited number of possibilities to do things is a solid guarantee that there will be a limited number of ways in which we could mess things up. Simply put, constraints are good.

Piped syntax

Let's define a multiplication function in JavaScript:

function multiply(a, b) {
  return a * b;
}

Let's now call it as shown in the following code:

multiply(8, "10");

In JavaScript, mixing types like we just did would work fine because the JS engine will coerce the string "10" into the number 10.

Let's now look at Elm. Navigate to the online Elm editor at http://elm-lang.org/try and type the following code into it:

import HTML exposing (text)

multiplyNumber x y = 
  x * y

main =
  text ( toString ( multiplyNumber 8 10 ) )

First, we import the HTML library. Then, we use the keyword exposing to make the text function available for us. The text function will convert Elm's text strings into HTML strings.

Next, we define the multiplyNumber function. It takes two parameters, x and y. Then, in our app's entry point, main, we simply pass the result of multiplyNumber to the toString function since we need to convert the number returned from multiplyNumber to a string in order for the text function to be able to use it. It is the  text function that will print out the result of the converted multiplyNumber to the web page. 

Let's look at our main entry point again:

main =
  text ( toString ( multiplyNumber 8 10 ) )

Using parentheses here, we specify the order of operations. Contrary to JavaScript, Elm does not use parentheses to list the parameters to be passed to a function. It uses spaces. Thus, to tell the multiplyNumber function to multiply 8 and 10, we simply write this code:

multiplyNumber 8 10

The only reason to use parentheses is to avoid ambiguity when we pass in the result of the preceding function to another function, which, in our example, is the toString function:

toString ( multiplyNumber 8 10 )

If we did not use the parentheses, our code would look like this:

toString multiplyNumber 8 10

Thus, the compiler would think that we are passing three parameters to the toString function—multiplyNumber, 8, and 10, which is obviously not what we are trying to do. Let's look at this line of code again:

  text ( toString ( multiplyNumber 8 10 ) )

It is obvious that the expressions are evaluated from the multiplyNumber function. The result is then returned, and thus used as a parameter of the toString function, and then finally the value returned from the toString function is used as the parameter of the text function, which prints the number 80 on the screen.

The preceding expression is still easily readable. But what if we had even more functions nested within other functions? What if, for the sake of seeing the effect of parentheses everywhere, we replaced the number 8 with multiples of 2? In other words, what if we did this:

  text ( toString ( multiplyNumber 2 ( multiplyNumber 2 ( multiplyNumber 2 10 ) ) ) )

Ok, so let's go through the expressions in order of execution, that is, starting with the innermost parentheses first:

 multiplyNumber 2 10

The preceding expression returns the value of 20. We than pass that value as the second argument to the next multiplyNumber:

multiplyNumber 2 ( multiplyNumber 2 10 )

This time, we multiply the number 2 with the value returned from the innermost multiplyNumber (which is 20). Finally, we run the third multiplyNumber, then the toString, and finally the text function to get the final result, which is still 80.

This is a simple example, and things are already starting to look a bit messy. What if we ran 20 different functions this way? Would our one line of code become 500 characters long? Or should we use word-wrap in our text editor? Even if we did that, it would still look and feel clunky.

This is where piped syntax comes to the rescue. Instead of reasoning about our code as we just described in the preceding code snippet, we can simply take the result of the first function and pipe its result to the next function. Like this:

import HTML exposing (text)

multiplyNumber x y = 
  x * y

main =
  multiplyNumber 2 10
    |> multiplyNumber 2
    |> multiplyNumber 2
    |> toString
    |> text

Feel free to take a moment and think about the reduced cognitive load of writing this piped syntax in Elm. Contrary to JavaScript, piped functions are just a part of the Elm language. To wrap up this section of our chapter, let's rewrite the starting example. This time, we will be using piped syntax:

import HTML exposing (text)

multiplyNumber x y = 
  x * y

main =
  multiplyNumber 8 10
    |> toString
    |> text

Another reason to love the piping syntax is, if we had a list of 20 functions, each piping into the next one, and we decided to, say, remove functions number 5, 7, and 15, we would simply erase the corresponding lines that have those pipes. Contrast that to messing with parentheses and making sure that they are all properly opened and closed.

Note that when we add several piping operators like we did in the preceding code snippet, what is actually happening is that the preceding pipe operator, |>, formally known as the forward function application operator, evaluates the expression on its left side and gives it as the last parameter to the function on its right side.

Easy refactoring

Simply put, refactoring is a fancy word for the simple process of changing how your code looks without changing the result it produces. For example, say that you have a function in your code that is getting too long. You can replace that one long function in your code with two smaller functions, each of them doing a more specialized task. However, the external behavior of that code will not change. By replacing parts of your application's code, you are either making it easier to use, easier to understand and reason about, or easier to maintain. All of these things are important reasons for refactoring.

Refactoring in Elm is easier because of the way the compiler works. The compiler will keep on reminding you of all the things you broke, until you fix them. Also, since Elm is a statically typed language, the type system it has in place will take care of a lot of quirks you would usually have to take care of yourself when using JavaScript. As we saw in the previous section, piping syntax is a core feature of the language, and this too, in some cases, can speed up refactoring.

Helpful type system

As mentioned earlier, Elm is statically typed. It has a concept of type annotations. These are not compulsory, but are preferred for the sake of clarity. If you look at the code that someone else wrote, it's always good to be able to look at type signatures. So, let's look at our little multiply numbers app, only this time with added type annotations:

import HTML exposing (text)

multiplyNumber: Int -> Int -> Int
multiplyNumber x y = 
  x * y

main =
  multiplyNumber 8 10
    |> toString
    |> text

Compared with other languages, whose type systems have verbose type declarations and strange error messages, and still have runtime errors, Elm's type system is not only helpful, it truly adds to developer happiness. Looking at the function signature for multiplyNumber, we can see that it takes two Int values, and returns an Int, so a quick glance will tell us exactly what is going on.

Increased productivity

This point is a logical conclusion we can derive from all the points we touched on in the preceding section. With super-fast virtual DOM, friendly compile-time errors, zero runtime exceptions, constraints as guarantees, piped syntax, and a helpful type system, Elm truly is a joy to work with. The compiler is your friend, and you'll find yourself writing code more confidently. With less of a need for unnecessary mental acrobatics, writing Elm code is simply more productive.

Getting started with writing Elm code

Navigate to the hello world example on the official online editor:http://elm-lang.org/examples/hello-HTML.

The code is as follows:

import HTML exposing (text)

main =
 text "Hello, World!"

The preceding code compiles to a simple Hello, World! output. But what is this output? Is it an HTML element? No. It is actually just a DOM text node. And since text nodes need to have an element attached to them, this text node gets attached to the topmost element that actually gets rendered, which is the <body> element. You can verify this by inspecting the rendered text in your browser's Developer Tools.

Let's do something else here. Let's render an actual HTML element on the page. To do this, we need to pass in a function with some attributes to our main variable. For example, we can do this:

import HTML exposing (..)
 main =
 h1 [] [ text "Hello, Elm!" ]

What did we do here? We passed in the h1 function to main. The h1 function takes two parameters; the first parameter is empty, and the second parameter takes in the text function as the attribute. This text function accepts a string of text as its own parameter, in this case, Hello, Elm!”.

Let's inspect the Developer Tools after this change. We can see that the text node's parent is now indeed an h1 HTML tag. Make sure to keep the Developer Tools open; we will being using it later.

Let's change the function from h1 to h2:

h2 [] [ text "Hello, Elm!" ]

Press the Compile button on the online editor, and you'll get the expected result—the text is now a bit smaller, with the Developer Tools showing that the parent of our text node is now indeed an <h2> HTML tag:

Let's try a different tag, for example, an anchor tag:

main =
 a [] [ text "Hello, Elm!" ]

What about anli? Refer to the following code snippet:

main =
 li [] [ text "Hello, Elm!" ]

Can we add it as a paragraph? Refer to the following code snippet:

main =
 p [] [ text "Hello, Elm!" ]

Nesting components is easy. For example, if we want to render a div that holds two paragraphs, we will call a div function and inside its brackets, we'll call two p functions, like this:

import HTML exposing (..)

main =
 div []
 [ p [] [text "1st paragraph" ]
 , p [] [text "2nd paragraph" ]
 ]

In all the examples so far, we left the first parameter empty. That parameter is used to add HTML attributes, for example, class. So, let's now try to color our div:

import HTML exposing (..)
import HTML.Attributes exposing (class)

main =
 div [ class "danger" ]
 [ p [] [text "1st paragraph" ]
 , p [] [text "2nd paragraph" ]
 ]

We gave our div the class of danger, and we'd like to give it the CSS declaration of background: red. But where do we add it?

The fastest way to do this is to use a nice Elm editor available online, that is Ellie.

Getting started fast with Ellie-app

Navigate to this address in your web browser: https://ellie-app.com/new. In the Elm section of code, paste in the Elm code that we discussed at the end of the previous section, where we introduced the class of danger to our div. In the HTML section of our code, add the following CSS just above the already included closing </style> tag:

.danger {
  background: red;
}

Click the Compile button, and you'll see the result in the window on the right-hand side inside of Ellie.

Now that we have successfully completed and compiled this very simple app, let's look at why the Ellie editor is better to use than the default Try Elm editor available on the official website.

Adding type annotations

Although we are able to add CSS to our app, unfortunately, we don't have compiler suggestions available in Ellie.

If a linter was available in Ellie, we would notice a warning on the main function, as the linter would show it as underlined. If we had the linter available, hovering over the warning underline would result in the following message showing in a popup:

Top-level value main does not have a type annotation.
 I inferred the type annotation so you can copy it into your code:
 main : HTML msg

Note

Interestingly, the Ellie app previously had the linter automatically enabled, but for some reason, it is currently not being used on the Ellie editor. Later on in this chapter, we'll see how to set up a more robust development environment for our Elm apps.

Let's pretend for a moment that we indeed do have a linter in our Ellie app, and include the preceding type annotation, so our code will look like this:

import HTML exposing (..)
import HTML.Attributes exposing (class)

main : HTML msg
main =
  div [ class "danger" ]
  [ p [] [text "1st paragraph" ]
  , p [] [text "2nd paragraph" ]
  ]

Note that we also exposed the class function in the preceding code. Click the Compile button again. As mentioned earlier, adding type annotations, while not compulsory, is considered a best practice. 

Note

At this point, just what the main : HTML msg means was left out on purpose. The goal of this chapter is to introduce you to the general ideas of how things work in Elm without covering all the details so that you can grasp the most important concepts and only then look at other, more difficult paradigms.

There are more features in Ellie that make it the best possible editor to start getting familiar with Elm. 

However, to make sure you get the most out of this introductory chapter, we will also look at setting up Elm using the wonderful create-elm-app npm package. 

Finally, we will wrap this chapter up by seeing how to set up Elm to work with a code editor (Atom from GitHub) on your computer.

Getting started fast with create-elm-app

To quickly make an Elm app using npm, you need to have Node and npm installed on your computer. The advantages of using the elm-app npm package is that the setup is very easy.

You simply install it through the command line, using the following command:

npm install create-elm-app -g

The preceding command will install the create-elm-app package globally on your system. 

Next, open the folder where you would like to have your Elm app installed. Let's say you want to call your app elm-fun. In that case, run the following command to install your Elm app:

create-elm-app elm-fun

Finally, to run your app, cd into the elm-fun folder, and run the following command:

elm-app start

Since your app contains no code yet, you'll just be greeted with an Elm logo. This is a confirmation that things are running fine. If you'd like to see your app do at least something else, try adding the following snippet of code in Main.elm:

import HTML exposing (..)
import HTML.Attributes exposing (class)

import HTML exposing (..)

main : HTML msg
main =
  div [ ]
  [ h1 [] [text "Elm is fun!" ]
  , p [] [text "Let's learn some more!" ]
  ]

With your elm-app start command still running in your console, it will recompile your project upon save and show you a simple web page.

To find out more about this npm package, point your browser to this URL: https://www.npmjs.com/package/create-elm-app

Getting started with Elm on Windows 10

Unfortunately, there are quite a few steps involved to get your Elm environment set up on Windows. Luckily, once it is set up, it is a pleasure to work with. In this section, we will cover all the steps that need to be taken to set up your Elm environment on Windows 10 as easily as possible. 

First, open your command prompt and run the following command:

npm install -g elm

Download Atom from https://atom.io and run the installer. Install language-elm via the Atom package manager (CTRL + ,) to get to settings, click Install Packages, and type language-elm. Enter the following command:

npm install -g elm-oracle

In powershell, where.exe elm-oracle will return this:C:\Users\PC\AppData\Roaming\npm\elm-oracleC:\Users\PC\AppData\Roaming\npm\elm-oracle.cmd

In Atom, type CTRL , to get to the settings. Once in Settings , click Packages, then in Installed Packages, filter by package name: elm. The package language-elm window will open; click its Settings, and inside this package's Settings, paste in the elm-oracle executable path.

Installing apm (Atom Package Manager)

This is the location to which Atom installs apm by default:

C:\User\PC\AppData\Local\atom\app-1.19.7C:\User\PC\AppData\Local\atom\app-1.19.7\resources\app\apm\bin

Now, typing apm in powershell will get us a number of options for the apm command, which means it's been added to the path successfully. Let's install atom-beautify using the apm command in our console:

apm install atom-beautify

Continue by installing elm-format:

apm install elm-format

Get Windows installer for Elm Platform from https://guide.elm-lang.org/install.HTML, click on the Windows installer links, and it will download Elm-Platform-0.18.exe. Run it to install the Elm Platform. Once the installation is done, click the Finish button to close the installation window. Make sure you add it to the path at C:\Program Files (x86)\Elm Platform\0.18\bin.

Now's the time to download elm-format. To get elm-format.exefor windows, navigate to https://github.com/avh4/elm-format/releases and scroll down to the Downloads section. Click on the link that pertains to your OS. In our example we are using Windows (specifically Windows 10), so we will click on the elm-format-0.18-0.7.0-exp-win-i386.zip download link.

In order to use elm-format in Windows, we need to point to the elm-format executable in our PATH variable. However, since the Elm platform installer points to its executables automatically (during the installation process, it adds a new variable to the PATH), all we need to do to effectively move the elm-format executable to our path is to paste the unzipped executable to C:\Program Files (x86)\Elm Platform\0.18\bin. In other words, we need to unzip it to the folder that has the Elm installation.

Note

If you are not sure where your Elm program is installed, run where.exe elm in Powershell.

Now open the Atom editor, go to Settings | Packages, and type elm in the Installed Packages search field.

The list of packages should include the elm-format package; click on its Settings. When the elm-format package settings open, in the Binary path field, paste in the path to your elm executable C:\Program Files (x86)\Elm Platform\0.18\bin\elm-format.exe and close the settings tab. No need to save, Atom does this automatically.

Now, you can test if elm-format works by running it on any Elm document. For the sake of argument, let's make a new Elm document that is poorly formatted:

module Main exposing (..)
import HTML exposing (HTML, text)
main =
text "Hello, Elm!"

Let's run elm-format on this file, from the console. First, you need to point your console to the folder in which the badly formatted Elm file sits, then run:

elm-format .\poorly-formatted-file.elm

You will get the following warning: 

This cannot be undone! Make sure to back up these files before proceeding.
Are you sure you want to overwrite these files with formatted versions? (y/n)

Type y to perform the formatting, then inspect the file to see the result. Let's now continue with our installation. To continue, we will install apm linter by running this command in our console:

apm install linter

Make sure to keep Atom open as it will install some dependencies right from the Atom interface, namely linter-ui-default and its default dependencies (intentions, busy-signal). Once it's done, you can proceed to run this command in your console:

apm install linter-elm-make

The preceding command will install linter-elm-make to a location similar to this:

C:\Users\PC\.atom\packages

Note: In the preceding example, PC is the username.

Now, let's take our setup for a test-drive. Make a new folder, let's call it elmtest. Inside the folder, create a file. Call it Main.elm, and open it in the Atom editor.

As soon as you do it, you'll get this alert:

No elm-package.json beneath or above the edited file.
You can generate an 'elm-package.json' file by running elm-package install' from the command line.

So, let's run elm-package install. First, we'll point our console to the elmtest folder, then run:

elm-package install -y

The console will report: Packages configured successfully!. It will also list the installed packages.

Now, let's add some code to Main.elm to make sure it works:

module Main exposing (..)
import HTML exposing (HTML, text)

main =
    text "Hello, Elm!"
 

Summary


In this chapter, we have covered a number of important topics, namely:

  • What is Elm?
  • What is unique about Elm in the saturated field of frontend web development?
  • How does Elm compare to JavaScript?
  • How to get started quickly with the help of the Ellie-app, the create-elm-app, and the Atom editor

In the next chapter, we will look into using elm-reactorelm-makeelm-repl, and elm-package, and we will start working with Elm code.

About the Author

  • Ajdin Imsirovic

    Ajdin Imsirovic is a full-stack web developer who has published several courses on the subject of web design and web development. He has also authored three books, Bootstrap 4 Cookbook, Elm Web Development, and Vue.js Quick Start Guide. In his fourth book, Vue CLI 3 Quick Start Guide, he introduces readers to the rich ecosystem of frontend tooling and best practices.

    Browse publications by this author

Latest Reviews

(3 reviews total)
Exceptionnel a ce tarif de 5€
Interesting book about interesting subject but all the examples aren't consistent with the new version Elm 0.19. I would give 5 star if there was possibility to download adjusted sources at least.
Books content and structure is good
Elm Web Development
Unlock this book and the full library for FREE
Start free trial