You are on page 1of 419

Elm Web Development

An introductory guide to building functional web


apps
using Elm

Ajdin Imsirovic
BIRMINGHAM - MUMBAI
Elm Web Development
Copyright © 2018 Packt Publishing
All rights reserved. No part of this book may be reproduced, stored in a retrieval system,
or transmitted in any form or by any means, without the prior written permission of the
publisher, except in the case of brief quotations embedded in critical articles or reviews.
Every effort has been made in the preparation of this book to ensure the accuracy of the
information presented. However, the information contained in this book is sold without
warranty, either express or implied. Neither the author, nor Packt Publishing or its
dealers and distributors, will be held liable for any damages caused or alleged to have
been caused directly or indirectly by this book.
Packt Publishing has endeavored to provide trademark information about all of the
companies and products mentioned in this book by the appropriate use of capitals.
However, Packt Publishing cannot guarantee the accuracy of this information.
Commissioning Editor: Smeet Thakkar
Acquisition Editor: Siddharth Mandal
Content Development Editor: Arun Nadar
Technical Editor: Rutuja Vaze
Copy Editor: Safis Editing
Project Coordinator: Sheejal Shah
Proofreader: Safis Editing
Indexer: Aishwarya Gangawane
Graphics: Jason Monteiro
Production Coordinator: Shantanu Zagade
First published: March 2018
Production reference: 1280318
Published by Packt Publishing Ltd.
Livery Place
35 Livery Street
Birmingham
B3 2PB, UK.
ISBN 978-1-78829-905-3
>www.packtpub.com
mapt.io

Mapt is an online digital library that gives you full


access to over 5,000 books and videos, as well as
industry leading tools to help you plan your
personal development and advance your career.
For more information, please visit our website.
Why subscribe?
Spend less time learning and more time
coding with practical eBooks and Videos
from over 4,000 industry professionals

Improve your learning with Skill Plans built


especially for you
Get a free eBook or video every month

Mapt is fully searchable

Copy and paste, print, and bookmark


content
PacktPub.com
Did you know that Packt offers eBook versions of
every book published, with PDF and ePub files
available? You can upgrade to the eBook version at
www.PacktPub.com and as a print book customer, you are

entitled to a discount on the eBook copy. Get in


touch with us at service@packtpub.com for more details.
At www.PacktPub.com, you can also read a collection of
free technical articles, sign up for a range of free
newsletters, and receive exclusive discounts and
offers on Packt books and eBooks.
Contributors
About the author
Ajdin Imsirovic has been working with frontend
technologies, as well as web and print design, for
almost 2 decades. He is an accomplished video
course creator and the author of Bootstrap 4
Cookbook, by Packt Publishing. In his second book,
Elm Web Development, he eases in the newcomers
to the Elm language in several fun projects.
I thank my wife Lejla for being supportive in writing this book.
Without her help, this book would not have been possible.
About the reviewers
Jasmin Djedovic is an experienced programmer
focusing mainly on C++, C#, and the Elm language.
He works as a teaching assistant at the faculty of
electrical engineering at the University of Tuzla. He
also works as a full-stack developer in a fintech
company, working on multiple desktop
applications used in stock exchange trading.
I would like to thank Ajdin for inviting me to this great journey of
publishing a book about Elm. Thanks to Amer Hasanovic, my
mentor who shared his knowledge and encouraged me to dig into
functional programming using Elm language. Last and not least,
thanks to my family, friends, and colleagues for being supportive.
Packt is searching for
authors like you
If you’re interested in becoming an author for
Packt, please visit authors.packtpub.com and apply
today. We have worked with thousands of
developers and tech professionals, just like you, to
help them share their insight with the global tech
community. You can make a general application,
apply for a specific hot topic that we are recruiting
an author for, or submit your own idea.
Preface
Elm is a wonderful functional programming
language for frontend web development. There are
a number of reasons why learning Elm will be a
useful and enjoyable experience.
Why learn Elm?
Elm is easy to get started with. The learning curve
is not too steep. Elm does not introduce an
overwhelming amount of new concepts to
understand and learn; actually, it is quite the
opposite. Once you learn the Elm way of doing
things, with the focus on some key concepts of
functional programming, there is not a huge
number of new topics to understand.
It can be a great way into the more advanced
concepts of functional programming. It’s probably
among the easier ways to start thinking in terms of
the functional programming paradigm.
Even though is it easy to learn, the return on
investment is pretty high; imagine all the time you
will save not having to do JavaScript-style
debugging! In the long run, this ROI compounds.
The way that Elm achieves this is by having no
runtime errors. All the errors are caught at compile
time (so you can forget about things like undefined
is not a function on a live project). Compile time is
very fast. Also, Elm has one of the friendliest and
most helpful compilers around.
If you are a bit tired of JavaScript and want to look
at alternatives, you will most likely love Elm. Along
the same lines, if you have had trouble trying to
make up your mind about whether you should
learn Angular, or React, or Vue, or any other JS
framework, Elm is a viable alternative.
If you are learning React, understanding the Elm
architecture will help you wrap your mind around
Redux. An interesting bit of trivia: Redux was
inspired by the Elm architecture.
If you already have some experience with
JavaScript, it will give you a new perspective on it,
and actually make you a better JavaScript
developer. You can even use Elm and JavaScript
together! Elm interops with JavaScript so you can
use both languages side by side in your projects.
Elm is declarative. Scaling your Elm apps is
actually a pleasant experience. Elm has a great
community and excellent documentation.
The goal of this book
This book was written with the goal of introducing
a complete beginner to the Elm language with
some of its basic concepts and taking those
concepts and building simple web apps in Elm
from the get-go.
This book does not cover all the features of the Elm
language, but rather gives you a solid foundation
that you can build from.
One of the goals of this book was to make it easy for
those who read it understand more advanced Elm
apps and topics. Another goal of this book was to
give you enough knowledge that you can
confidently build your own apps in Elm once you
complete it.
Who this book is for
This book is intended for web developers of all
levels of expertise to use to understand the Elm
language and build simple and practical web
applications in it.
The beauty of the Elm language, among other
things, is in the fact that you don’t need to worry
too much about HTML and CSS when you write it.
So in this book, the focus is mostly on the Elm
language itself.
Familiarity with HTML, CSS, and the DOM, as well
as how the web works, would be helpful, but is not
necessary. Also, some experience with the
Bootstrap framework and Material Design
Language is welcome, but not compulsory.
This book was written with the goal of finding a
sweet spot between giving enough knowledge so
that readers can be productive as soon as possible,
and being overwhelming.
The book was also written with the idea that
someone who is brand new to web development
should find it easy to understand. Therefore, the
book doesn’t assume knowledge of JavaScript, nor
does it extensively compare JavaScript with Elm, or
if it does, it tries to limit this comparison to the
level of broad generalizations.
In a sentence, this book is aimed at beginners and
experienced developers alike, regardless of their
level of knowledge of JavaScript.
What this book covers
, Why Is This a Great Time to Learn Elm?,
Chapter 1

covers introductory topics: what is Elm, what is


unique about it, how it compares to JavaScript,
how to get started with it, and some very basic Elm
concepts with simple code snippets.
, Building Your First Elm App, describes
Chapter 2
immutable data structures, the Elm architecture
(model, view, and update), unidirectional data
flows, using HTML functions in Elm, expressions
in Elm (if-else and case expressions), Elm data
structures, union types, and some operators.
, Creating Your Personal Portfolio with
Chapter 3

Elm, discusses type aliases, mapping and filtering


values, adding Bootstrap 4 styles to your Elm apps,
understanding function signatures, and
modularizing your Elm apps.
, Preparing a Unit Conversion Website in
Chapter 4

Elm, looks at using the Result type as a way to


handle errors, and reinforces concepts covered in
the previous chapters in a fun project.
, Completing the Unit Conversion Website
Chapter 5
in Elm, explains using Html.map, working with
complex views and multiple models, and shows
how to improve the existing Unit Conversion
website.
, Exploring Elm in Greater Detail, covers
Chapter 6
the topic of destructuring values in Elm, the ways
in which Elm deals with randomness (through
commands and subscriptions), understanding
partial application, and working with Html.program.
, Making a Weather App in Elm, shows how
Chapter 7
to deal with errors using Result, how to deal with
optional values and nothingness using Maybe, how to
decode JSON strings, how to fetch remote data
with the Http package, and how to work with third-
party APIs.
, Adding More Features to the Weather
Chapter 8
App, shows how to make our Elm apps look nicer
by adding elm-mdl and how to work with the Round
module, and reinforces topics covered in previous
chapters.
, Testing in Elm, covers Elm unit testing
Chapter 9
and the role of the describe, Expect.equals, and test
functions; it also deals with some specific operators
and ways to use them in Elm tests, how to write
tests for decoding JSON, and introduces fuzz tests.
, Integrating Elm with a Web Framework,
Chapter 10
shows how to integrate Elm apps in Rails 5.1 using
the Webpacker and Yarn, as well as set everything
up in a web-based IDE using an Ubuntu container.
To get the most out of
this book
1. To get the most out of this book, you should
be familiar with HTML, CSS, the way the
web works, Bootstrap 4, and MDL.
2. Elm installation and setup are covered in Cha
pter 1, Why is This a Great Time to Learn

Elm?, and will be updated in the code that


comes with the book in the case of any
changes, updates, or errata.
Download the example
code files
You can download the example code files for this
book from your account at www.packtpub.com. If you
purchased this book elsewhere, you can visit www.pack
tpub.com/support and register to have the files emailed
directly to you.
You can download the code files by following these
steps:

1. Log in or register at www.packtpub.com.


2. Select the SUPPORT tab.
3. Click on Code Downloads & Errata.
4. Enter the name of the book in the Search
box and follow the onscreen instructions.

Once the file is downloaded, please make sure that


you unzip or extract the folder using the latest
version of:

WinRAR/7-Zip for Windows

Zipeg/iZip/UnRarX for Mac

7-Zip/PeaZip for Linux

The code bundle for the book is also hosted on


GitHub at https://github.com/PacktPublishing/Elm-Web-Develop
ment. In case there’s an update to the code, it will be
updated on the existing GitHub repository.
We also have other code bundles from our rich
catalog of books and videos available at https://githu
b.com/PacktPublishing/. Check them out!
Conventions used
There are a number of text conventions used
throughout this book.
: Indicates code words in text, database
CodeInText

table names, folder names, filenames, file


extensions, pathnames, dummy URLs, user input,
and Twitter handles. Here is an example: “At this
time, you might start to appreciate the work
that elm-format does for us.”
A block of code is set as follows:

main : HTML Never


main =
view

Any command-line input or output is written as


follows:
[3,6,9] : List Int

Bold: Indicates a new term, an important word, or


words that you see onscreen. For example, words
in menus or dialog boxes appear in the text like
this. Here is an example: “Upon compilation, the
Ellie app will show the words User
Experience in the right-hand preview pane.”
Warnings or important notes appear like this.

Tips and tricks appear like this.


Get in touch
Feedback from our readers is always welcome.
General feedback: Email feedback@packtpub.com and
mention the book title in the subject of your
message. If you have questions about any aspect of
this book, please email us at questions@packtpub.com.
Errata: Although we have taken every care to
ensure the accuracy of our content, mistakes do
happen. If you have found a mistake in this book,
we would be grateful if you would report this to us.
Please visit www.packtpub.com/submit-errata, selecting your
book, clicking on the Errata Submission Form link,
and entering the details.
Piracy: If you come across any illegal copies of our
works in any form on the Internet, we would be
grateful if you would provide us with the location
address or website name. Please contact us at
copyright@packtpub.com with a link to the material.

If you are interested in becoming an author:


If there is a topic that you have expertise in and you
are interested in either writing or contributing to a
book, please visit authors.packtpub.com.
Reviews
Please leave a review. Once you have read and used
this book, why not leave a review on the site that
you purchased it from? Potential readers can then
see and use your unbiased opinion to make
purchase decisions, we at Packt can understand
what you think about our products, and our
authors can see your feedback on their book. Thank
you!
For more information about Packt, please visit packt
pub.com.
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
SyntaxError, URIError, RangeError, 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

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


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

As I infer the type of values flowing through your program, I


HTML
String

feels like we have a friend in there


As I infer
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 ( mul

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 an li? 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: http
s://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
main : HTML msg

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.
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-oracle
C:\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.7
C:\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 http


s://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.exe for windows, navigate to https://github.com/av

h4/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.

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 befor
Are you sure you want to overwrite these files with formatted

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-pa
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-


reactor, elm-make, elm-repl, and elm-package, and we will
start working with Elm code.
Building Your First Elm
App
Welcome to the second chapter. The goal of this
chapter is to understand the following topics:

Immutable data structures

Elm Architecture basics—working with


Model, View, and Update

Messages in Elm

Unidirectional data flows

Understanding the beginnerProgram function

Reinforcing the use of HTML functions in


Elm

If-else expressions in Elm

Case expressions in Elm

Basics of Elm data structures (lists, tuples,


records, sets, arrays, and dictionaries)

Union types in Elm

Using modulus to perform calculations in


Elm

After completing this chapter, you will be able to do


the following things:
Work with the Elm architecture

Use if-else expressions and case expressions


in your apps

Build a very simple Fruit Counter app in


Elm

Build a very simple FizzBuzz app in Elm


Let’s build an app
In this section, we will discuss immutable data
structures and the basics of the Elm architecture.
To make things more practical, we will build a very
simple app, which will serve our goal of reinforcing
these important concepts.
Immutable data
structures
Elm is a functional programming language. One of
the tenets of functional programming languages is
that data structures are immutable. Once created,
they cannot be changed. In practice, this means
that functions in Elm will take in a data structure
as its argument, and then return an entirely new
data structure.
If you think about it, it makes perfect sense. If all
the data structures are immutable, how do we deal
with change? More specifically, how do we deal
with changes that Elm functions introduce on the
existing data? The only obvious answer is to create
entirely new data.
How does Elm apply this in practice?
Let’s say that we want to build an app in the health
niche. The app is a simple countdown app that
displays only a button and a number. The app
starts with the number 5. The idea of the app is for
a user to press a button whenever they have eaten a
healthy snack, say, a piece of fruit. That way, a
user will improve their healthy habits by making
sure that they have eaten five pieces of fruit a day.
The Elm architecture
The very minimum that we need to understand
about the Elm architecture is that it is made up of
four things: Model, View, Message, and Update.
It is important to note that the architecture is
usually described as being made up of only three
things: Model, View, and Update. However, for the
sake of clarity and easier learning, at this point of
our journey into Elm, we can think of the Message
as an equal building block of the Elm architecture.
In later chapters, as we dive deeper into both
theory and practice, we will clarify these
distinctions. However, for the sake of having a clear
mental model of all the things that are in play here,
we will think of the Elm architecture as having four
constituent pieces.
The Model
The Model holds our app state. The Model in our
Fruit Counter app is very simple: it holds, as its
one and only data structure, an integer. When we
first run our app, the Model holds the value of 5.
Since it is represented in terms of data structures,
and since data structures in Elm are immutable,
our model will have to be updated as a copy of the
previous data plus changes made to it. The model is
updated whenever a function operates on it.
When will a function operate on the model?
Since we are building a very simple app, the only
time a function will operate on the model is when
the user clicks the one button in our app. Clicking
this one button will decrement the current value in
the model by 1. Since data in Elm is immutable, a
function will have to return a new copy of the
model, with updates taken into account.
The View
The View can be thought of as the way to see the
Model on our screen. The View is a function, and
we pass the Model to it. Thus, the View takes in the
Model as its parameter, and returns HTML, which
will be rendered in the browser.
Another way to think of the View is that it is a way
to allow the user to interact with the Model. To use
the API reference we made in the previous chapter,
the View is sort of like a visual API for the Model. It
is a way for the user to manipulate the Model in a
structured way.
When a user interacts with the View, they
manipulate the Model by changing its state. Since
our app is very simple, the only way to change the
state is to press that one button in our app, which
will decrement the current value that our model
holds.
This one button press will make the View send a
Message to the Update.
The Message
After the user has interacted with the View
(pressed the button), the notification of this action
will be sent to the Update function in the form of a
Message. Since there is only one possible action
that can happen, that Message is simply:
decrement.
Of course, in any realistic app, the Message will
have to have a lot more logic, but for the sake of
simplicity, let’s leave it at decrement.
The Update
The Update function receives the Message. Next,
the Update function determines how to update the
state of our app, that is, the Model, based on the
Message received. Once the update is made, a new
Model is created and it renders the View. The user
then interacts with the View which results in the
Message being sent again. The Update receives the
Message and updates the Model, and the cycle
continues.
Unidirectional data
flows
An interesting way of looking at our app is as flows
of data. The reason to have this concept and use it
in our discussions is because it is a helpful way of
looking at the changing of state in our Elm app.
Since Elm is built on a lot of restrictions, it only
makes sense to have this idea of restrictions
applied to its architecture as well.
If you look at what is happening in our simple app,
you will notice that the data always moves in only
one direction: From Model to View to Message to
Update to Model. That’s what unidirectional data
flow basically is.
This concept allows us to be able to track state
changes with ease, and it also makes it a lot less
difficult to reason about these changes.
Our app requirements
At this point of the book, we are more than ready to
build our first app:

We have an idea of how functional


programming works

We understand the most basic concepts of


how the Elm architecture operates (Model,
View, Message, Update)

We know what we want our app to do

Armed with this knowledge, we can now build our


app.
Building the Fruit
Counter app
Let’s begin with our app skeleton. Let’s point our
browser to Ellie-app.com, and we’ll be greeted with
this code:
module Main exposing (main)

import HTML exposing (HTML, text)

main : HTML msg


main =
text "Hello, World!"

The preceding code will be the starting point for


our app. We’ll build on top of it by slowly adding
features to it, and explaining the underlying
concepts at the appropriate times, just when we
need to understand them.
Exposing everything
we need
Let’s begin by updating module Main to expose
everything, by adding two dots inside parentheses.
We will do the same for the imported HTML module,
since we want do be able to use all the available
HTML functions. Specifically, we need access to
the h1, p, and button functions.
Next, the line that reads main : HTML msg is an optional
type annotation, so to demonstrate that we can
work without it, we’ll comment it out by placing
two hyphens and a space at the beginning of this
line.

Including type annotations is considered a best


practice, and we have commented it out only to show
that our app will still run without a type annotation
(as well as to demonstrate how to add one-line
comments in Elm).

Our app now looks like this:


module Main exposing (..)

import HTML exposing (..)

-- main : HTML msg


main =
text "Hello, World!"

To preview the app at this stage, simply compile it


in the Ellie-app.
The Model
Let’s add our model:
-- MODEL

type alias Model =


Int

Our Model is just a simple integer type.

For now, we’ll simply ignore what this type alias


signifies, as it will only distract us from understanding
fundamental concepts at the moment. We will return to
types later on in the book.
The View
Our View function will receive the current model and
will return the following HTML:
-- VIEW

view model =
div [] [ h1 [] [ text ("Fruit to eat: " ++ (toString mode

In Chapter 1, Why Is This a Great Time to Learn


Elm?, we looked at rendering some basic HTML to
the screen using Elm’s HTML functions. To
reinforce that concept in light of the preceding
code, let’s quickly discuss the HTML that our view
function will render.
The code that we are assigning to the view function
is located to the right of the equals sign (Elm’s
assignment operator). In this code, we are running
the div function. Like all other HTML functions in
Elm, the div function has two pairs of square
brackets. The first pair of square brackets
optionally lists the HTML attributes for the div
function, and the second one lists the content of the
actual div.
We have left the first pair of square brackets empty,
which means we have not given any attributes to
our div function. Then, inside the second pair of
square brackets of the div function, we have passed
in the h1 function .
Like all other HTML functions, the h1 function also
has two pairs of square brackets. In the preceding
example, our h1 function has no attributes specified
(as the first pair of brackets is empty - just like we
did with the div, its parent function/element).
Inside the second pair of brackets of the h1
function, we call the text function.
The text function will render a text node. The text to
be output inside the text node is surrounded by
parentheses. Inside the parentheses, we are using a
string literal and we are concatenating to it the
value of the model, converted to a string (using the
toString function).

We’ve just learned about one of Elm’s operators,


the ++ operator.
In Elm, ++ is the string concatenation operator, used to
join together two separate strings.

Since we will initialize our model with the value of


5, the preceding code for the view function will
initially return the following HTML code:
<div>
<h1>Fruit to eat: 5</h1>
</div>

Our view function is now ready at the most


rudimentary level. Next, we’ll deal with the
Message section.
The Message
Let’s now look at the Message section, where we’ll
declare a new type, and we’ll call it Msg:
-- MESSAGE

type Msg =
Decrement

As mentioned earlier in the chapter, we will not deal


with explaining types at this time.
The Update
It’s time to add our update. In the previous step, we
have declared our special type of Msg. We’ll pass it to
the update function:
-- UPDATE

update msg model =


model - 1

By now, it should be easy for you to guess what the


update function will do: it will accept a msg and a model,
and it will return a copy of the model, decremented
by 1.
At this point of our Fruit Counter app
development, all we have to do is make the Model,
View, and Update work together, and for that, we’ll
use the beginnerProgram function.
Adding the
beginnerProgram
function
It’s time to add the beginnerProgram function, which we
will assign to the main function.
Our main function currently looks like this:

-- main : HTML msg


main =
text "Hello, World!"

Let’s add our updated main function, which will now


have the beginnerProgram function assigned to it:

-- main : HTML msg


main =
beginnerProgram { model = 5, view = view, update = update }

As you can see, we just call the beginnerProgram


function. Then, we pass it the model, view, and update
functions, and assign each of them a value.
We initialize the model with the value of 5. For the
update, we assign it to the value of the update function.
The view is similarly set to the value of the view
function.
After we made these changes to our main function,
the full code of our app now looks like this:
module Main exposing (..)
import HTML exposing (..)

-- main : HTML msg


main =
beginnerProgram { model = 5, view = view, update = update }

-- MODEL
type alias Model =
Int

-- VIEW
view model =
div [] [ h1 [] [ text ("Fruit to eat: " ++ (toString mode

-- MESSAGE
type Msg =
Decrement

-- UPDATE
update msg model =
model - 1

If we ran our app at this point, everything would


work, and we would get the following output on our
screen: Fruit to eat: 5.
Even though our app is really basic, it’s great that
everything works and that we are not getting any
compiler errors at this time. However, there is one
thing we did not do, and that is that we have not
added the button, the one entry point to initiate the
changing of our app’s current state.
Before we do add this button, feel free to look at the
preceding code and think a bit about a perfectly
stateless app. Currently, our app’s model will never
change, as the update section of our code will never
be run.
Let’s rectify that by adding a button.
Views, buttons, and
events
Let’s begin by simply adding a static button to our
app. We’ll do that by updating the view function
with the following code:
-- VIEW
view model =
div []
[ h1 [] [ text ("Fruit to eat: " ++ (toString model))
, button [ onClick Decrement ] [ text "Eat fruit" ]
]

If you saved and ran the app right now, the


compiler would throw the following error:

NAMING ERROR
Line 23, Column 20
Cannot find variable onClick

Why did we get this error? Because we haven’t


imported the onClick function. Let’s do that now, by
adding the import at Line 3 of our app.
Looking at the beginning of our code, this is what
the first three lines of code should look like after
the update:
module Main exposing (..)

import HTML exposing (..)


import HTML.Events exposing (onClick)

Running our app now would give us a simple,


rudimentary, but working app built with Elm!
When you click the Eat fruit button, the view
function hits the first pair of square brackets on the
button function, and it watches for the click event.
We have provided the onClick function with the Msg to
be sent once the button is clicked. Since there is
only one possible message in our app, once the
click event is triggered, the view function will send
the Decrement message to the update function.
Once the update function has received the message, it
will return a new model, and the new model gets
rendered by the view function.
However, there is an issue. If we keep clicking the
button, our app will ultimately move into counting
negative numbers, and that’s just impossible. A
user should not have negative 2 fruits left to eat.
In the following section, we’ll fix this issue.
Constraining the
Decrement message
In order to constrain the Decrement message, let’s
begin by looking at the current update function:

-- UPDATE
update msg model =
model - 1

Now, let’s introduce an if-else statement to cater for


different possible scenarios:

-- UPDATE
update msg model =
if model > 0 then model - 1 else model == 5

Unfortunately, the preceding code does not


produce the desired results. Instead, we get this
compiler message:
TYPE MISMATCH
Line 39, Column 5
The branches of this if produce different types of values.

The then branch has type:

number
But the else branch is:

Bool
Hint: These need to match so that no matter which branch we t

We briefly touched upon Elm constraints in Chapter


1, Why Is This a Great Time to Learn Elm? The
preceding issue is a great example of Elm
constraints in practice. Because of the way Elm is
set up, each branch must return the same data
type. In our case, we can either return Booleans for
either branch of the if-else expression, or return
numbers, but we can not have them mixed up.
So, how do we rectify this? To keep things simple
and still use an if-else expression we started with,
let’s think of a terse way to avoid type mismatch.
Our counter value should never go under zero, so
we can simply do this:
-- UPDATE
update msg model =
if model > 0 then model - 1 else model + 5

Using the preceding code, our if-else logic will


always return a number. The update function’s
message value will be incremented by 1 as long as
it’s greater than zero. Otherwise (in case it is zero),
it will be increased by 5.
To wrap this section up, let’s look at another way
we could have written the main function in our
code:

main =
HTML.beginnerProgram
{ model = 5
, update = update
, view = view
}

Before we finish this section of our chapter, let’s


look at the completed Fruit Counter app, as we will
refer to it later in the chapter:
module Main exposing (..)

import HTML exposing (..)


import HTML.Events exposing (onClick)

-- main : HTML msg


main =
HTML.beginnerProgram
{ model = 5
, update = update
, view = view
}

-- MODEL
type alias Model =
Int

-- VIEW
view model =
div []
[ h1 [] [ text ("Fruit to eat: " ++ (toString model))
, button [ onClick Decrement ] [ text "Eat fruit" ]
]

-- MESSAGE
type Msg =
Decrement

-- UPDATE
update msg model =
if model > 0 then model - 1 else model + 5

Now that we have looked at a practical


implementation of a simple app, let’s discuss some
of the theoretical concepts behind it, namely values
and types in Elm, functions in Elm, and if
expressions. We will wrap it up with a more in-
depth look at Elm messages.
Values, expressions,
data structures, and
types in Elm
There are no statements in Elm. In the if-else
example we saw earlier in this chapter, the
structure that we used was an expression, not a
statement.
This difference is important, because it tells us
something about the behavior of the Elm language:
an expression will always return a value. In fact,
everything in Elm is an expression, and thus
everything will return a value, even without having
to use the return keyword explicitly (like we have to
do in JavaScript, for example).
What, then, can this value be? A value is just a
result of a calculation. It is the result of running an
expression. In other words, when an expression is
evaluated, it will result in a value. To test this out,
we can have a look at the Elm REPL. To keep
things simple, we will use the online elm-repl,
available at elmrepl.cuberoot.in.
The Elm REPL is an interpreter for the Elm language.
It’s important to distinguish between the interpreter
and the compiler.

Why are we using Elm REPL in the following


examples? We are using it because it is a
straightforward way to get information on the type
of value that each of the expressions we give the
REPL will evaluate to. In other words, we will give
the Elm REPL a number of expressions, mostly in
the form of simple values, and the REPL will give
us back their type. We will start with expressions
whose values will evaluate to primitive types.
Primitive types in Elm
The primitive types in Elm include Char, String, Bool,
and number (Int and Float).
Char and String types
Let’s type the following inside the online elm-repl:
'a'

The Elm-repl will give us back the result of running


the preceding expression, and follow it up with the
type of that value. The type of value ‘a’ is a Char:

'a' : Char

Let’s do another one, this time with double quotes:


"a"

What we get back is this:


"a" : String

We can read the preceding as: Value ''a'' has a type


of String.
Obviously, when we use single quotes, we get Chars.
To get the type of String from a value, we need to
surround that value in double quotes.

Multiline strings are written by enclosing any


number of lines in three consecutive double quote
characters. When using REPL, each line must also
end with a backslash. Elm REPL inserts the pipe
characters automatically. The entered multiline
string value will still evaluate to the type of String:

""" \
| This
| is
| a
| multi-line
| string
| """
" \n This \n is \n a \n multi-line \n string \n " : String

Let’s look at other primitive types in Elm.


The number type
If we just type a number, we’ll get back that same
number, followed with a colon and the number type:
> 5
5 : number

If we test a decimal number, we’ll get back the type


of Float:

> 3.6
3.6 : Float

Why are some types capitalized, and some are not?

If a type is capitalized, it means it is an explicit type.


Basically, the number type is used for both Ints and Floats.
Which one it will end up being (which explicit type it
will end up being), depends on how that number is
used.

Put differently, number is an implicit type, since it can


end up as an explicit Int or an explicit Float.

To get back a value of type Int, let’s run the


following command:

> truncate 3.14


3 : Int
Booleans
Let’s look at Boolean values:
> True
True : Bool
> False
False : Bool

Capitalization is important! For example, typing


true will throw an error:

> true
-- NAMING ERROR ---------------------------------------------

Cannot find variable `true`

3| true
^^^^
Maybe you want one of the following?

List.take
String.trim

With this, we have covered the primitive types in


Elm. Next, we are going to look at data structures.
Since Elm language is statically typed, and since
everything in Elm is an expression, it follows that
whatever we put into REPL will in fact be an
expression, with a value that will evaluate to a type.
Data structures are part of this behavior too.
Data structures: lists,
tuples, records, sets,
arrays, and dictionaries
Basic data structures in Elm are lists, tuples,
records, sets, arrays, and dictionaries. In this
section, we will look at the behavior of each of these
data structures, using Elm REPL and the Ellie-app.
Lists
A list in Elm is like an array in JavaScript. For our
first example, let’s type this value in Elm REPL:
[ 1, 2, 3, 4 ]

This is what we get back from the REPL:


[1,2,3,4] : List number

Awesome, a List of numbers!


Contrast that with Floats:
[ 0.1, 0.2, 0.3, 0.4 ]

The REPL responds with:


[0.1,0.2,0.3,0.4] : List Float

Let’s try Chars:

[ 'a', 'b', 'c' ]

The REPL returns this:

['a','b','c'] : List Char

However, mixing values in Lists in Elm is not


allowed:
> [ 1, 2, 3, 'c' ]
-- TYPE MISMATCH --------------------------------------------
The 3rd and 4th entries in this list are different types of v

3| [ 1, 2, 3, 'c' ]
^^^
The 3rd entry has this type:

number

But the 4th is:

Char

Hint: Every entry in a list needs to be the same type of valu


never run into unexpected values partway through. To mix diff
single list, create a "union type" as described in:
<http://guide.elm-lang.org/types/union_types.HTML>

Evidently, we cannot mix types in Elm Lists. In


other words, each List in Elm is a group of
expressions that must always evaluate to the same
type.
Let’s try Strings:

> [ "just", "a", "bunch", "of", "strings" ]


["just","a","bunch","of","strings"] : List String

What about an empty list?:


> []
[] : List a

List a means that this list is empty, that is, that it


can hold anything. This wraps up our short
overview of Lists in Elm. Next, we will look at
tuples.
Tuples
In Elm, a tuple is a data structure that can hold
values of various types.
To make a Tuple in Elm REPL, let’s simply put a
String and a Boolean inside parentheses:

( "abc", True )

The REPL will respond with:


("abc",True) : ( String, Bool )

A tuple can hold a maximum of nine values.


Interestingly, tuples of different lengths are
considered to be of different types. For example,
let’s make a List that holds two tuples, using Elm
REPL:

[ ( 'a', 'b' ), ( 'c', 'd' ) ]

This expression will evaluate to:


[('a','b'),('c','d')] : List ( Char, Char )

What REPL tells us is that the value we put in is a


List of two tuples, holding values of Char type.

Let’s try to vary the number of Chars in the second


tuple:
[ ( 'a', 'b' ), ( 'c' ) ]
Running the preceding code in the REPL will throw
the following error:
-- TYPE MISMATCH --------------------------------------------

The 1st and 2nd entries in this list are different types of v

3| [ ( 'a', 'b' ), ( 'c' ) ]


^^^
The 1st entry has this type:

( Char, Char )

But the 2nd is:

Char

Elm looks at the expression we gave it, and comes


back with the Type Mismatch error. Indeed, for two
tuples to be considered to be of the same type, they
have to hold the same number of values, and those
values also need to be of the same type.
In the preceding working example, we added two
tuples of two Chars to a List, and Elm REPL
returned List ( Char, Char ).
The maximum number of values a tuple can hold in
Elm is 9. If you try to add 10 or more values to a
tuple, Elm will throw an error. Let’s try this out:

('1','2','3','4','5','6','7','8','9','0')

What we get back is the following error:


elm-make: Could not find `_Tuple10` when solving type constra

In the chapters that follow, we will get to know


tuples in much greater depth. For now, let’s just
mention a common use case: since tuples as a data
structure can hold various values, they are useful as
a way to store results of calculations.
Records
Records in Elm use curly braces, and a label for
each value must be provided. Records can also hold
multiple values. For example, we can type the
following record into Elm REPL:
{ color="blue", quantity=17 }

The REPL will return this:


{ color = "blue", quantity = 17 } : { color : String, quantit

We will use records a lot in our Elm programs, as


records allow us to model the data in a wide variety
of scenarios.
We have already used a record in our code in this
chapter, in the Adding the beginnerProgram
function section. Let’s recall the code we used:
-- main : HTML msg
main =
beginnerProgram { model = 5, view = view, update = update }

To test out the code in REPL, we will only have to


use the record, without all the fluff:
{ model = 5, view = view, update = update }

REPL will respond with the following error:


-- NAMING ERROR ---------------------------------------------

Cannot find variable `update`


3| { model = 5, view = view, update = update }
^^^^

The REPL will follow up the preceding error with a


similar error saying that the 'update' variable cannot
be found either. To rectify this, as an exercise, we
can give values to the variables used in our record,
as follows:
> view = "view info"
"view info" : String
> update = "update info"
"update info" : String
> { model = 5, view = view, update = update }
{ model = 5, view = "view info", update = "update info" }
: { model : number, update : String, view : String }

We have assigned values of type String to the view


and update variables in the Elm REPL. Then we
entered the record, and the REPL returned types
for each of the variables used in the record. Thus,
in the preceding example, the model is of type number,
the update is of type String, and the view is also of type
String.

Next up, we’ll look at sets, arrays, and dictionaries,


which have to be imported, although they too are
built into Elm.
Sets
Sets are collections of unique values. Their
uniqueness is guaranteed by the Elm programming
language. We can instantiate sets as empty sets or
use the fromList function. Creating an empty set is
easy: set = Set.empty.
Let’s look at the other way of creating sets in Elm,
by pointing our browser to: ellie-app.com/new.
The page that opens already has some Elm code in
it. Let’s adjust that code so that it looks as follows:

module Main exposing (main)

import HTML exposing (HTML, text)


import Set

set = Set.fromList [1,1,1,2]

main : HTML msg


main =
text (toString set)

What we did in the preceding code was, after


importing Set (to the variable we named set), we
assigned the returned value from the evaluated
expression: Set.fromList [1,1,1,2].
Next, we gave the set variable to our main function,
to render it out as a text node. Of course, before it
could be rendered out, we had to convert it to
a String. After pressing the Compile button in the
Ellie-app, we should see the following
result: Set.fromList [1,2].
Sets are useful when we are trying to find
differences between data structures. Next, we’ll
look at arrays.
Arrays
Arrays in Elm are zero-based, just like they are in
JavaScript. With arrays, we can work with
elements based on their index. Like sets, arrays can
be created using the fromList function.
Alternatively, we can create an empty array like
this: array = Array.empty. Still in the Ellie-app, let’s
make a slight change to our code to test out arrays:
module Main exposing (main)

import HTML exposing (HTML, text)


import Array

array = Array.fromList [1,1,1,2]


array2 = Array.get 0 array

main : HTML msg


main =
text ((toString array) ++ " " ++ (toString array2))

In the preceding code, we have a slight twist—we


grouped the concatenation of two arrays and a
space, all converted to Strings, and then ran the text
function on them, finally passing the value
returned from the evaluation of the expression to
the main function.
The compiled code will display the following
result: Array.fromList [1,1,1,2] Just 1. For now, let’s just
ignore what this result means, as we’ll get back to it
later in the book. Next, we’ll look at dictionaries in
Elm.
Dictionaries
Dictionaries are also created using the fromList
function. Let’s revisit the Ellie-app, with our code
changed as follows:
module Main exposing (main)

import HTML exposing (HTML, text)


import Dict

dict =
Dict.fromList
[ ("keyOne", "valueOne")
, ("keyTwo", "valueTwo")
]

main : HTML msg


main =
text (toString dict)

After compilation, the Ellie-app will return this:

Dict.fromList [("keyOne","valueOne"),("keyTwo","valueTwo")]

Dict is the data structure used to store pairs of keys


and values. Keys must be unique. To learn more
about this data structure, visit the following URL: h
ttp://package.elm-lang.org/packages/elm-lang/core/latest/Dict.

Next, we’ll look at how types work for functions


and if expressions in the Elm language.
Functions, if
expressions, and types
Let’s create a new function in Elm REPL. We’ll call
our function multiplyBy5:

multiplyBy5 num = 5 * num

The REPL will return this:

<function> : number -> number

The preceding line says that our multiplyBy5 function


has the type of number -> number. Let’s see what type
will get returned from a function that works with
Strings:
appendSuffix n = n ++ "ing"

As we already know, the ++ operator is the concat


operator in Elm; it will join two Strings together.
Thus, expectedly, Elm REPL will return:

<function> : String -> String

As we can see, the preceding function is of type


String -> String.

But, what is this String -> String? And, along the


same lines, what is the Int -> Int from the previous
example? String -> String simply means that the
function expects a String as its argument, and will
also return a String. For the Int -> Int example, the
function expects a value of type Int and will also
return a value of type Int.
It’s time to take a look at the basics of types in if
expressions in Elm. Consider the following snippet
of code and the response REPL gave it:

> time = 24
24 : number
> if time < 12 then "morning" else "afternoon"
"afternoon" : String

In the preceding code, we are running an if


expression using the variable time (which we assign
the value of 24). Then, we are running our
comparison. Note that if expressions should
actually be referred to as if-else expressions, as if
expressions must have an else, otherwise they
won’t work in Elm. Both the if and the else branch
must be of the same type. That’s why in the
preceding example we are making sure that either
result we get is of type String.
Revisiting Elm
messages
We started off this chapter by looking at the Elm
arhitecture: Model, View, and Update. We also
mentioned another important ingredient:
Messages, a way for Views to communicate
with Updates.
We used a very simple example app, Fruit Counter.
The app was simple indeed: the only message our
view could ever send to the update function was
Decrement. The simplicity of the app we made was a
great way for us to understand the architecture
without having to introduce too many concepts
that would get in the way of learning.
However, now that we have a rudimentary
understanding of all the moving parts and how
they fit together, we can talk about another level of
complexity related to messages in the Elm
arhitecture.
To do that, we can look at a finished example
straight from the official documentation. This will
serve two purposes: first, it will get us in the habit
of referring to the excellent official docs as often as
possible, and second, it will give us a benchmark
that we will initially look up to, and during the
course of the book, leave behind as we learn more
advanced concepts.
To get started, open the documentation for the
buttons example at: https://guide.elm-lang.org/architectur
e/user_input/buttons.HTML.
Next, visit the Ellie online editor at https://ellie-app.c
om/new, and paste in the code from the buttons

example:
import HTML exposing (HTML, button, div, text)
import HTML.Events exposing (onClick)

main =
HTML.beginnerProgram { model = model, view = view, update =

-- MODEL
type alias Model = Int
model : Model
model =
0

-- UPDATE
type Msg = Increment | Decrement
update : Msg -> Model -> Model
update msg model =
case msg of
Increment ->
model + 1

Decrement ->
model - 1

-- VIEW
view : Model -> HTML Msg
view model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] [ text (toString model) ]
, button [ onClick Increment ] [ text "+" ]
]

After pasting in the code, click the Compile button


in the right-hand pane and the app will compile
and run there. The app itself is very similar to our
Fruit Counter, only slightly more advanced. Let’s
compare these differences.
In Fruit Counter, we had the following message:

-- MESSAGE
type Msg =
Decrement

In the Buttons app, the message is as follows:


-- UPDATE

type Msg = Increment | Decrement

The first thing to note is the comment preceding


the code. Initially, we used MESSAGE as the comment
text. However, in the Buttons example, they are
using UPDATE in their inline comment. The reason:
conventionally, the Elm architecture is considered
to consist only of Model, View, and Update. We
used Message as a separate part of the architecture
only to help understand it easier. The most
important point of that understanding is: the
View sends Messages to the Update
function. Conceptually, this is the same in both
examples. The only difference is that now the
Message is defined in the Update section of our
app, as it should be.
The second thing to note is that our message only
had the value of Decrement. It could only be a Decrement
message. In the Buttons app, we have two options:
the Message can be either a Decrement or an Increment.
What’s with the type keyword, and the pipe
character, then? It has to do with something known
as union types (also known as algebraic data
types or tagged unions).
In Elm, a union type is simply a custom type that
we can come up with on the fly. In our Fruit
Counter app, our Msg union type has only one value:
Decrement. In the Buttons app, the Msg union type can
have either of the two values: Increment or Decrement.
To differentiate clearly between possible values in a
union type, we use the pipe character.
Let’s make another custom union type in the Elm
REPL:
type Vehicle = Car | Bike | Boat | Helicopter

To create a union type, we begin with the type


keyword. Next, we provide the actual type, Vehicle.
We created a custom type on the fly, and named it
Vehicle! To the right of the assignment operator (the
= sign), we provide the values that the Vehicle union
type can have. These values are called type
constructors, as you can use them to construct
new instances of Vehicle.
Let’s create a new instance of Vehicle in the REPL:
> friendsRide = Helicopter
Helicopter : Repl.Vehicle

We have just constructed a new instance of


the Vehicle type. As we can see, the REPL responds
with this information—the value is Helicopter, and its
type is Vehicle.
Sometimes, it is helpful to explain the same
concept in a couple of different ways. Another way
of looking at union types is that they are a way for
us to describe constructor functions, that is, to
define them.
In the update function, we had:
type Msg = Increment | Decrement

The preceding code means that to make a Msg, either


the Increment function or the Decrement function needs
to be called.
The theoretical underpinnings of union types are
rooted in mathematical logic, namely the set
theory, which is basically the study of collections of
things. Thus, we can look at a union type as a
combination of any number of collections of things.
Both union types and the set theory can get quite
abstract, but at this point in our learning, suffice it
to say that union types are a way to organize
messages in Elm apps.
Functions, pattern
matching, and case
expressions
The goal of this chapter was to build a simple app
and learn important theory behind it. We expanded
on this goal by comparing our own app with the
one from the official docs.
In this section, we will look at the update function
of the Buttons App and take it apart in order to
have complete understanding of what it does and
how it works.
This is important, because once we understand
how the update function works in the Buttons app,
we will be able confidently to implement a similar
solution and improve our Fruit Counter.
Let’s begin by inspecting the update function in the
Buttons app:

update msg model =


case msg of
Increment ->
model + 1

Decrement ->
model - 1

We see a new keyword here: case.


Generally, the way that the case syntax works is as
follows—a variable has a certain value. Based on its
value, a certain block of code will execute. When
the value is different, the block of code to execute
will be different as well. Finally, at the end of a case
expression, there is a block of code that will execute
for all the values that were not already specified in
the case expression. In other words, for any
unspecified scenario, there is a case block at the
bottom to take care of it. This case block is called the
wildcard and it’s marked with the underscore
character, _. As we can see in the preceding
example, there are situations where the wildcard
case does not need to be added, because we have
already covered all the possibilities.
Elm case expressions are evaluated via pattern
matching, that is, by verifying whether a case
conforms to a pattern. If expressions and case
expressions are quite similar. One major
difference is that case expressions match patterns,
and if expressions check for true conditions as
ways to determine which code blocks to run.
Looking at the syntax of case expressions, we can
see that they start with the case keyword, followed
by the name of the case expression (in our example,
msg). The name is completely arbitrary; instead of
msg, we could have used anything else. For example:

update whatever model =


case whatever of
Increment ->
model + 1

Decrement ->
model - 1

As you can see in the preceding code snippet, the


first parameter of the update function and the name
of the case expression must be the same. To avoid
confusion, it’s best to stick with msg as the first
parameter here, as that is the norm, and you’ll see
it used that way in most Elm programs.
So, after the case keyword, and the name of the case
expression, we have another keyword, of.
Next, we list our cases. The structure of the code is
always the same:
Pattern -> Expression to evaluate

If we look at the first case, we can see that it’s


written as follows:
Increment ->
model + 1

In the preceding code snippet, the pattern to match


is Increment, and the expression to evaluate is model +
1.
Improving the Fruit
Counter app
Based on everything we have learned so far, let’s
improve the Fruit Counter app. Here is the full
code:
module Main exposing (..)

import HTML exposing (..)


import HTML.Events exposing (onClick)

-- main : HTML msg


main =
HTML.beginnerProgram
{ model = 5
, update = update
, view = view
}

-- MODEL
type alias Model =
Int

-- VIEW
view model =
div []
[ h1 [] [ text ("Fruit to eat: " ++ (toString model))
, button [ onClick Decrement ] [ text "Eat fruit" ]
, button [ onClick Reset ] [ text "Reset counter" ]
]

-- MESSAGE
type Msg = Decrement | Reset

-- UPDATE
update msg model =
case msg of
Decrement ->
if model >= 1 then
model - 1
else
5
Reset ->
5

Let’s highlight the improvements we made to our


app:

1. We added another button to our view


function.
2. We added a new message, Reset, to be sent
when the new button is clicked.
3. We added the Reset constructor function to
our Msg union type.
4. In our update function, we gave our case
expression the name of msg, and we also gave
it two patterns to match: Decrement and Reset.
5. If the Decrement pattern gets matched, an if
expression will be evaluated (to determine
whether the model should be decreased by
one or whether its value should be 5).
6. If the Reset pattern gets matched, an
expression of 5 will be evaluated.

In the preceding explanation, an effort was made to


describe all the updates in accordance with the
concepts we learned in this chapter. It is important
to understand the preceding explanation as it is the
foundation on which we will build more complex
apps in chapters that follow.
Building a simple
FizzBuzz app in Elm
FizzBuzz is a word game for kids. The purpose is to
teach math, namely division.
The game is simple: each player calls out a number,
starting from 1. If a number can be divided by
three, a player needs to call out Fizz instead of a
number. Furthermore, if a number can be divided
by five, a player needs to call out Buzz instead of a
number.
Finally, if a number can be divided by both 3 and 5,
a player needs to call out FizzBuzz.
Since this is a well-known game, and a relatively
simple problem, it is a great way to test the level of
knowledge of a programmer.
In order to go about solving this problem, we will
introduce a new operator, the modulus operator, %.
This operator returns the division remainder.
The way it works can be best described by an
example. Open up the online Elm REPL, and run
the following expression:
12 % 10

The REPL will return:


2 : Int

What this means is: if we divide 12 by 10, we will get


1, and what will remain is 2. Since 2 is less then 10, it
will not be divided, as the modulus in our example
will only divide in increments of 10, and return the
remainder.
That’s why we said that the modulus operator
returns the remainder of a division (that is,
whatever is on the right side of the modulus
operator determines the size of increments).
So, for example, if we run this:
10 % 9

We will get this back:


1 : Int

However, we will get the exact same result if we


run this:
19 % 9

The preceding expression will evaluate to 1 : Int


because the size of the increments that will be
calculated is 9 in the preceding example. Since 9 * 2
= 18, the remainder of the division is 1.
What will happen if we divide the number by itself?
Let’s try number 3:
3 % 3

The REPL returns the following:

0 : Int

We get back the value 0, of type Int. We can do the


same for 5 and 15:
> 5 % 5
0 : Int
> 15 % 15
0 : Int

Again, we get back zero, of type Int.


What does this mean in practice? It means that we
can use an if-else expression to check for all the
numbers that do not have a remainder when
divided by 3. For all the numbers that satisfy that
condition, we will return Fizz. We will apply a
similar approach for numbers 5 and 15, as dictated
by the rules of the game.
This is enough information for us to create our
simple FizzBuzz app. Navigate your browser to ;http
s://ellie-app.com/new and type the following code:

module Main exposing (main)

import HTML exposing (text)

fizzBuzz = "FizzBuzz"
fizz = "Fizz"
buzz = "Buzz"

fizzBuzzInput value =
if value % 15 == 0 then
fizzBuzz
else if value % 3 == 0 then
fizz
else if value % 5 == 0 then
buzz
else (toString value)

main =
text (fizzBuzzInput 34567)

At this point, we have enough knowledge fully to


understand the preceding code with ease. Looking
at the preceding code, one of the important
concepts to be reminded of is the principle that we
need to obey when working with if expressions:
they should always return the same type of value.
That is why we are passing the value to the toString
function at the very bottom of our if expression.
As a quick reminder of piping syntax (discussed in
Chapter 1, Why Is This a Great Time to Learn Elm?),
here is another way we could have written the main
function:
main =
fizzBuzzInput 34567
|> text

The |> is called the forward function application


operator.

We have achieved our goal of making a working


FizzBuzz app in Elm. In the chapters that follow,
we will look at ways of making improvements to
our simple FizzBuzz app.
Summary
In this chapter, we have covered a number of
important topics, namely:

Elm syntax: values, types, data structures,


if-else expressions, case expressions, and

some operators

TEA: The Elm architecture

The concept of unidirectional data flows

Working with Elm REPL and the Ellie-app

We also built two working apps, which, although


simple, have shown the practical application of the
theoretical concepts we covered.
In the next chapter, we will make our own personal
portfolio in Elm.
Creating Your Personal
Portfolio with Elm
Welcome to Chapter 3, Creating Your Personal
Portfolio with Elm. The goal of this chapter is to
create a simple personal portfolio website in Elm.
The topics we will cover include:

Understanding Type Aliases

Discussing the Model, View, Message,


Update workflow in detail (a detailed look
into the Elm Architecture)

Mapping and filtering Lists of values


with List.map and List.filter

Using elm-make-app

Adding Bootstrap 4 styles to our app

Working with the HTML module in Elm

Understanding function signatures for


HTML elements

Modularizing our app by splitting the views


into multiple files

Using case expressions to dynamically


render sections of a web page

Improving our FizzBuzz app


using List.map and String.concat
After completing this chapter, you will be able to:

Work with type aliases in Elm

Know how to use the Elm architecture

Be able to manipulate Lists in Elm with


List.map and List.filter

Build Elm apps with elm-make-app

Modularize Elm apps into multiple files

Use case expressions for dynamic


updates to your Elm-powered web
pages

Add custom styles to your Elm apps


Creating our portfolio
with elm-make-app
To start this chapter, let’s remember that in Chapter
1, Why Is This a Great Time to Learn Elm?, we
have set up a workflow with the Atom editor. The
beauty of this setup is in the fact that, although
complicated, once the setup is complete, we have
all the additional Elm tools at our disposal.
To follow the instructions in this chapter, it’s not
absolutely necessary to use the said Atom setup, but it
would make it easier to work with our Portfolio app.
Alternatively, you can use any editor as these steps will
still work, but in that case you’d be working without
the linter and the syntax highlighter.

Thus, to start, let’s create a folder for our new


project using the console. For example, we can
point our bash to Desktop, and make a new folder
there:
mkdir chapter3

Next, let’s change the directory to chapter3 by using


the following command in our console: cd chapter3.
In Chapter 1, Why Is This a Great Time to Learn Elm?, we
discussed several ways to create a new Elm app. One
of them was using the create-elm-app npm package. If you
don’t already have the package installed, refer back to C
hapter 1, Why Is This a Great Time to Learn Elm?, to

make sure you’ve set everything up correctly.

Using the create-elm-app npm package, let’s create a


new Elm app, called my-portfolio:
create-elm-app my-portfolio
The console will print out the result of running the
preceding command, as follows:
Creating my-portfolio project...

Starting downloads...

● elm-lang/HTML 2.0.0
● elm-lang/virtual-dom 2.0.4
● elm-lang/core 5.1.1

Packages configured successfully!

Project is successfully created in `C:\Users\PC\Desktop\chapt

Inside that directory, you can run several commands:

elm-app start
Starts the development server.

elm-app build
Bundles the app into static files for production.

elm-app test
Starts the test runner.

elm-app eject
Removes this tool and copies build dependencies, configur
and scripts into the app directory. If you do this, you c

We suggest that you begin by typing:

cd my-portfolio
elm-app start

Once your new Elm app is created, open the my-


portfolio folder in the Atom editor.

From the Atom editor, twirl-open the src folder,


and open the Main.elm file. Delete the existing code,
and paste in the following code:
module Main exposing (main)

import HTML exposing (HTML, text)

main : HTML msg


main =
HTML.h1 []
[ text "My Portfolio" ]

Alternatively, you could import the h1 function


directly, and change the main function accordingly,
as follows:
module Main exposing (main)

import HTML exposing (HTML, h1, text)

main : HTML msg


main =
h1 []
[ text "My Portfolio" ]

The difference between the above two examples is in


the functions that we are importing. If we don’t import
the h1 function, we need to use HTML.h1.

Finally, using your console, cd into the my-portfolio


folder and run the following command:
elm-app start

Running the preceding command will open your


default browser with the URL of localhost:3000. On
the page that opens, you’ll see a nicely formatted h1
heading: My Portfolio. In the next section, we’ll
give our site some more content.
Building a static single-
page portfolio
We are building a simple single-page site to
showcase a writer’s portfolio. This should be a fun
little exercise in which we will understand how to
actually build websites with Elm. At the same time,
we have removed a level of complexity as our site
will consist almost entirely of text. Of course, as we
progress with Elm, we will introduce more
concepts, but for now, we’ll stick with the basics.
Let’s expand our site by adding a few more sections
to our home page. We will begin by making an
error. The following code will not work:
module Main exposing (main)

import HTML exposing (HTML, div, h1, text)


-- NOTE: we imported the 'div' function here so we can use it

main : HTML msg


main =
h1 []
[ text "My Portfolio" ]
div
[]
[ text "Just a bunch of text here " ]

We’ll get the following error when we save the


preceding code:
Function `h1` is expecting 2 arguments, but was given 5.

8| h1 []
9| [ text "My Portfolio" ]
10|> div
11|> []
12|> [ text "Just a bunch of text here " ]
Maybe you forgot some parentheses? Or a comma?

Detected errors in 1 module.

To resolve the issue, we need to structure the code


we pass to the main function a bit differently.
Basically, we need to have a wrapping HTML
element for all our HTML. We’ll wrap everything
we pass to main inside the wrapping div function:

module Main exposing (main)

import HTML exposing (HTML, div, h1, text)

main : HTML msg


main =
div []
[ h1 [] [ text "My Portfolio" ]
, div [] [ text "Just a bunch of text here " ]
]

Now, we can add to our portfolio using a number of


other elements. To make them all available, we will
expose everything inside the HTML module. The
updated code will now look like this:

module Main exposing (main)

import HTML exposing (..)

main : HTML msg


main =
div []
[ h1 [] [ text "My Portfolio" ]
, p [] [ text "Just Another Writer's Portfolio" ]
, div []
[ ul []
[ li [] [ text "About Me" ]
, li [] [ text "Poems" ]
, li [] [ text "Stories" ]
, li [] [ text "Contact" ]
]
]
]

At this time, you might start to appreciate the work


that elm-format does for us. No matter how bad our
formatting of Elm code is, it will fix it for us on
every save.
Before going any further, let’s add a CSS
framework to our single-page site. For this
example, we will be using the most recent version
of the Bootstrap framework, namely Bootstrap
4. In order to obtain the link to the CSS of the
framework, navigate to the following web page: http
s://getbootstrap.com/docs/4.0/getting-started/introduction/?#cs
.
s

The preceding link points to an HTML link element


that serves Bootstrap 4 from a Content Delivery
Network (CDN). There is even a handy copy
button on the right-hand side. Once you copy the
link, navigate to the public folder of your project,
and paste in the copied link tag in the index.HTML file.
The place to paste in the link tag is just above the
title tag, inside the head of the document. The
mentioned section of our index.HTML file should look
like this after the update:
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.
<title>Elm App</title>
</head>

Once that is done, save the index.HTML file and close it.
Back in Main.elm, we need to import the HTML.Attributes
module. We also need to specify the CSS classes to
use. We’ll begin by adding the .card class to the div
that wraps the ul tag.
The updated code should look like this:
module Main exposing (main)

import HTML exposing (..)


import HTML.Attributes exposing (..)

main : HTML msg


main =
div []
[ h1 [] [ text "My Portfolio" ]
, p [] [ text "Just Another Writer's Portfolio" ]
, div [ class "card" ]
[ ul []
[ li [] [ text "About Me" ]
, li [] [ text "Poems" ]
, li [] [ text "Stories" ]
, li [] [ text "Contact" ]
]
]
]

After this update to our code, you will see a slight


change to the ul element in our page. Let’s take it a
step further by passing several Bootstrap 4 CSS
classes to our div. To do that, we’ll simply replace
this code:
, div [ class "card" ]

Now, let’s change the preceding line by adding the


following code:

, div [ class "card text-white bg-primary mb-3" ]

After saving Main.elm, you should see a nice blue


background on the ul element, with the li items
colored white.
However, we are using the wrong kind of Bootstrap
component. We are using a card, but it would be
much more suitable to use the nav component,
available at https://getbootstrap.com/docs/4.0/components/nav
s/#horizontal-alignment, with the following HTML code:

<ul class="nav justify-content-center">


<li class="nav-item">
<a class="nav-link active" href="#">Active</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#">Disabled</a>
</li>
</ul>

However, the preceding code is plain HTML, and


we need to turn it into Elm code.
It is a nice exercise to try to convert the
aforementioned HTML code to Elm code by
yourself. However, there is an online converter
available at: http://mbylstra.github.io/HTML-to-elm/. We
can simply paste in the HTML code to the left pane
of the provided URL.
The code we get back, in the right pane, will look
like this:

ul [ class "nav justify-content-center" ]


[ li [ class "nav-item" ]
[ a [ class "nav-link active", href "#" ]
[ text "Active" ]
]
, li [ class "nav-item" ]
[ a [ class "nav-link", href "#" ]
[ text "Link" ]
]
, li [ class "nav-item" ]
[ a [ class "nav-link", href "#" ]
[ text "Link" ]
]
, li [ class "nav-item" ]
[ a [ class "nav-link disabled", href "#" ]
[ text "Disabled" ]
]
]

Now, we can simply paste in the preceding code in


place of the child div we had earlier. The complete
code will now look like this:
module Main exposing (main)

import HTML exposing (..)


import HTML.Attributes exposing (..)

main : HTML msg


main =
div []
[ h1 [] [ text "My Portfolio" ]
, p [] [ text "Just Another Writer's Portfolio" ]
, ul [ class "nav justify-content-center" ]
[ li [ class "nav-item" ]
[ a [ class "nav-link active", href "#" ]
[ text "Active" ]
]
, li [ class "nav-item" ]
[ a [ class "nav-link", href "#" ]
[ text "Link" ]
]
, li [ class "nav-item" ]
[ a [ class "nav-link", href "#" ]
[ text "Link" ]
]
, li [ class "nav-item" ]
[ a [ class "nav-link disabled", href "#" ]
[ text "Disabled" ]
]
]
]

Before we continue, we’ll have to do some


housekeeping and explain a few other important
concepts.
HTML for Elm
Up to this point in our journey through Elm, we
have not referred to the official docs in any
meaningful manner. In this section, we will focus
on the official documentation and look at the
practical ways it can help us.
To start, navigate to the official README for the
HTML Elm package, available at:
http://package.elm-lang.org/packages/elm-lang/HTML/2.0.0/. As
it says on the website, the page we have opened
describes the core HTML library for Elm. At the
current stage of our learning, the best way to grow
our Elm skills is to look at the actual HTML
package, available at: http://package.elm-lang.org/package
s/elm-lang/HTML/2.0.0/HTML.

Specifically, we will look at the type definitions of


all the HTML functions we have used in this
chapter: div, h1, text, p, ul, li, and a. Being able to
grasp the signatures of functions we use in our
programs fully will make it that much easier to
reason about them.
Function signatures for
HTML elements
All HTML elements share the same function
signature pattern:

<element> : List (Attribute msg) -> List (HTML msg) -> HTML m

Every HTML element takes in two lists: a List of


Attributes and a List of Children elements. Then,
they return an HTML msg. If the value returned from a
function does not emit a message, that code will
return the msg type.
In other words, every element returns a value of
HTML. This HTML value is of type msg, because
they end up as plain HTML nodes and they will not
(cannot!) change our app’s state.
That’s why it is perfectly possible to just keep
writing Elm’s HTML functions in our main function
and never add the view or update, and still have a
working web page. It is possible because we are
rendering our code without any messages, so
effectively, there is never going to be anything to
update, and thus, we can do without the update
function. That’s why we can replace the implicit
type of msg with a:
main : HTML a

By convention, a stands for anything. Since the main


function will never return a message, we can be
more explicit about it, and have the following code
as the function signature:
main : HTML Never

Now, we are explicitly declaring that we will never


return a message. The Type of Never cannot ever be
constructed.
Being able to grasp the signatures of functions we use
in our programs fully will make it that much easier to
reason about them.
Function signature for
text function
The text function’s signature, as we can see in the
official documentation, is as follows:

text : String -> HTML msg

Thus, the text function takes a String and returns an


HTML msg.
Adding our view
function
In this section, we will take out our view code from
directly passing it to the main function, and instead
assign it to the view function, which will then be
passed to the main function. This way, we are
beginning to make our code more modular and
reusable.
First, in our portfolio app, let’s rename the main
function to view function:

view : HTML Never


view =

Next, at the very bottom of the file, we can simply


add an updated main function:
main : HTML Never
main =
view

A nice thing about this setup is that we can now


pass our entire view to a wrapping div, for example,
like this:

main : HTML Never


main =
div [] [ view ]

We can also begin to make our view even more


modular and reusable by calling it from another
file.
Calling the view
function from another
file
To begin, let’s make a new folder inside the src
folder of our project. Inside your console, from the
root of your project, run the following command:

cd src && mkdir View

Next, cd into View and create a new View.elm file by


using the touch command. If you have the atom
command available in your console, you can open
the View.elm file right from your command line:
cd View && touch View.elm && atom View.elm

What does the preceding command do? It navigates to


the View folder via command line, then creates a new file
called View.elm, and then opens it in Atom. You don’t
have to use the command line at all—you can complete
these actions using the graphical user interface of your
operating system.

Because we are now putting our view function in a


separate file, we will effectively use the view
function as an Elm module. Since we will be using
our View.elm file as a module, we need to be explicit
about all the HTML functions we will use, similar to
how it works in the original Main.elm file. Thus, our
View.elm code needs to look like this:

module View.View exposing (view)

import HTML exposing (..)


import HTML.Attributes exposing (..)
view : HTML Never
view =
div []
[ h1 [] [ text "My Portfolio" ]
, p [] [ text "Just Another Writer's Portfolio" ]
, ul [ class "nav justify-content-center" ]
[ li [ class "nav-item" ]
[ a [ class "nav-link active", href "#" ]
[ text "Active" ]
]
, li [ class "nav-item" ]
[ a [ class "nav-link", href "#" ]
[ text "Link" ]
]
, li [ class "nav-item" ]
[ a [ class "nav-link", href "#" ]
[ text "Link" ]
]
, li [ class "nav-item" ]
[ a [ class "nav-link disabled", href "#" ]
[ text "Disabled" ]
]
]
]

Next, let’s update our Main.elm file, so that it looks


like this:

module Main exposing (main)

import HTML exposing (..)


import HTML.Attributes exposing (..)
import View.View exposing (..)

main : HTML Never


main =
div [] [ view ]

Save everything and the app will still work,


although with a warning from our linter, saying:
Module HTML.Attributes is unused.
Best to remove it. Don't save code quality for later!
Basically, what’s happened here is that we are
importing HTML.Attributes exposing (..) in our Main.elm,
but we are not using it at all. There are several ways
we can deal with this. For example, we can simply
pass only the view function to the main function. Or,
we could add a class of, for example, bg-warning to our
div:

module Main exposing (main)

import HTML exposing (..)


import HTML.Attributes exposing (..)
import View.View exposing (..)

main : HTML Never


main =
div [ class "bg-warning" ] [ view ]

Feel free to try out other solutions to solve this


warning, as it is a useful little exercise.
Adding some style
To continue, let’s make an intentional error. If
you’ve seen the result of using the bg-warning class on
our Portfolio app, you’ll see that it looks like an
orange stripe, and everything else below is just
plain old white. Let’s say we’d like to increase the
height of our wrapping div, so that it covers the
height of 1,000 pixels, thus coloring everything
orange. To achieve this in vanilla HTML, we could
use the style attribute. Let’s do that with Elm.
Without knowing the signature of the style function
that Elm uses, let’s just try to solve it without
looking at the docs:
main : HTML Never
main =
div [ class "bg-warning", style "height:1000px" ] [ view

As you’ve probably guessed, saving the preceding


code results in an error. Let’s look at the linter
message we get in Atom:

The argument to function style is causing a mismatch.


Function style is expecting the argument to be:
List(String,String)
But it is:
String

Great! Now, we know how we need to change our


style function. It needs to take one or more pairs of
Strings, in parentheses, just like the compiler said

previously. So, we’ll update our function like this:

main : HTML Never


main =
div [ class "bg-warning", style [("height","1000px")] ]
Now, everything works again, and the height of our
orange background has increased. Even better, to
make sure that our screen is completely orange,
let’s update the style again:

main : HTML Never


main =
div [ class "bg-warning", style [("height","100vh")] ] [

Instead of using pixels, we are using 100vh, that is,


100 viewport height, which translates to the full
height of the screen. In the next section, we will
make our view even more modular, by splitting it
into multiple files.
Splitting the view into
multiple files
In this section, we will split our view into multiple
files. We will also add additional content to it, so
that when we are finished, we have a complete
single-page site.
To begin, let’s think about what part of our view
code we can split into a separate file. It’s obvious
that the ul function is a separate chunk of code, so
let’s turn it into a module. In your View folder, open
a new file and call it Navigation.elm. Next, paste in the
following code:
module View.Navigation exposing (navigation)

import HTML exposing (HTML, a, li, text, ul)


import HTML.Attributes exposing (class, href)

navigation : HTML Never


navigation =
ul [ class "nav justify-content-center" ]
[ li [ class "nav-item" ]
[ a [ class "nav-link active", href "#" ]
[ text "Active" ]
]
, li [ class "nav-item" ]
[ a [ class "nav-link", href "#" ]
[ text "Link" ]
]
, li [ class "nav-item" ]
[ a [ class "nav-link", href "#" ]
[ text "Link" ]
]
, li [ class "nav-item" ]
[ a [ class "nav-link disabled", href "#" ]
[ text "Disabled" ]
]
]
We have just built a new module called
View.Navigation. Save the file, and go back to View.elm,
where we will import this new module.
Back in View.elm, our updated code will look like this:
module View.View exposing (view)

import HTML exposing (..)


import HTML.Attributes exposing (..)
import View.Navigation exposing (..)

view : HTML Never


view =
div []
[ h1 [] [ text "My Portfolio" ]
, p [] [ text "Just Another Writer's Portfolio" ]
, navigation
]

Notice the change in this version of View.elm—we


have imported the View.Navigation module, exposing
everything, and we have also put the navigation
function in place of the modularized ul [] [] code.
We can follow this pattern to build up our entire
page. The next thing that our page needs to have is
a couple of poems.
To add them, we will use the Bootstrap 4 card
component. Thus, let’s make a new file in our View
folder, call it Poem.elm, and add this code to it:
module View.Poem exposing (poem)

import HTML exposing (HTML, div, h4, p, small, text)


import HTML.Attributes exposing (class)

poem : HTML Never


poem =
div [ class "card" ]
[ div [ class "card-header" ]
[ text "Summer poetry collection " ]
, div [ class "card-block" ]
[ h4 [ class "card-title" ]
[ text "Title of Poem" ]
, p [ class "card-text" ]
[ text "Lorem ipsum." ]
, p [ class "card-text" ]
[ text "Dolor." ]
, p [ class "card-text" ]
[ text "Sit amet." ]
, p [ class "card-text" ]
[ text "Consectetur." ]
, p [ class "card-text" ]
[ text "Adipiscing elit." ]
, p [ class "card-text" ]
[ text "Adipiscing elit." ]
, p [ class "card-text" ]
[ small [ class "text-muted" ]
[ text "Last updated 3 mins ago" ]
]
]
]

Next, import the Poem module inside the View.elm


file:
module View.View exposing (view)

import HTML exposing (..)


import HTML.Attributes exposing (..)
import View.Navigation exposing (..)
import View.Poem exposing (..)

view : HTML Never


view =
div []
[ h1 [] [ text "My Portfolio" ]
, p [] [ text "Just Another Writer's Portfolio" ]
, navigation
, poem
]

If you look at the website right now, you will see


that the poem is stretching the entire width of the
screen, which is not the best possible look. Let’s
wrap the poem inside a container, div, by updating
our View.elm file:
view : HTML Never
view =
div [ class "container-fluid" ]
[ div [ class "container" ]
[ h1 [] [ text "My Portfolio" ]
, p [] [ text "Just Another Writer's Portfolio" ]
, navigation
, poem
]
]

Let’s also change our code so that we can fit more


than one card on a single row. We’ll do that by
adding another default Bootstrap 4 class, row. Inside
the row, we’ll add three columns of equal widths. To
do this, we’ll update the View.elm file as follows:
view : HTML Never
view =
div [ class "container-fluid" ]
[ div [ class "container" ]
[ h1 [] [ text "My Portfolio" ]
, p [] [ text "Just Another Writer's Portfolio" ]
, navigation
, div [ class "row" ]
[ div [ class "col" ]
[ poem ]
, div [ class "col" ]
[ poem ]
, div [ class "col" ]
[ poem ]
]
]
]

If you followed all the steps correctly up to this


point, you should see the following web page in
your browser:
Now, we are fitting three cards nicely on a single
row on our page. Before we continue building our
page, it’s time to make it better looking. Back in
Main.elm, let’s erase the class function, and add

another CSS declaration to the style function:


main : HTML Never
main =
div [ style [ ( "height", "100vh" ), ( "background", "#ee

This will give our page a much calmer, light gray


background.
The style function takes a List of 2-element tuples. In
the preceding example, the List consists of two 2-
element tuples, where each element in the tuple is of
type String. Thus, the first tuple is ("height", "100vh"), and
the second one is ("background", "#eee").

In the next section, we will finish our page by


adding the remaining view files.
Completing our Writer’s
portfolio
In this section, we will finish our Writer’s portfolio.
Before we continue, let’s list what it is that we still
need to add.
As we mentioned at the beginning of the chapter,
the completed portfolio should have these sections:

About Me

Poems

Stories

Contact

We have completed the Poems section. Now, let’s


add the remaining sections. As we already know
how this is done, let’s batch our processes to speed
up development.
First, inside the View folder, let’s create three new
files:

About.elm

Stories.elm

Contact.elm

Next, let’s import all of these into View.elm:


module View.View exposing (view)

import HTML exposing (..)


import HTML.Attributes exposing (..)
import View.Navigation exposing (..)
import View.About exposing (..)
import View.Poem exposing (..)
import View.Stories exposing (..)
import View.Contact exposing (..)

Next, let’s add some content to each of the files we


just made. First, the About.elm file:
module View.About exposing (about)

import HTML exposing (..)


import HTML.Attributes exposing (class, href)

about : HTML Never


about =
div [ class "" ]
[ h1 [ class "display-2 mt-5" ]
[ text "John Doe" ]
, h3 [ class "display-5" ]
[ text "A man of words" ]
, p [ class "" ]
[ text "Etiam in libero gravida, viverra ex eu, v
]

Next, let’s update the Stories.elm file:

module View.Stories exposing (stories)

import HTML exposing (..)


import HTML.Attributes exposing (class, href)

stories : HTML Never


stories =
div [ class "" ]
[ h1 [ class "display-2 mt-5" ]
[ text "Stories" ]
, h3 [ class "display-5" ]
[ text "A List of My stories" ]
, p [ class "" ]
[ text "Etiam in libero gravida, viverra ex eu, v
]

Also, let’s update the Contact.elm file:


module View.Contact exposing (contact)

import HTML exposing (..)


import HTML.Attributes exposing (class, href)

contact : HTML Never


contact =
div [ class "card" ]
[ div [ class "card-header" ]
[ text "Summer poetry collection " ]
, div [ class "card-block" ]
[ h4 [ class "card-title" ]
[ text "Title of Poem" ]
, p [ class "card-text" ]
[ text "Lorem ipsum." ]
, p [ class "card-text" ]
[ text "Dolor." ]
, p [ class "card-text" ]
[ text "Sit amet." ]
, p [ class "card-text" ]
[ text "Consectetur." ]
, p [ class "card-text" ]
[ text "Adipiscing elit." ]
, p [ class "card-text" ]
[ text "Adipiscing elit." ]
, p [ class "card-text" ]
[ small [ class "text-muted" ]
[ text "Last updated 3 mins ago" ]
]
]
]

We’ll also need to update the View.elm file:

view : HTML Never


view =
div [ class "container-fluid" ]
[ div [ class "container" ]
[ h1 [] [ text "My Portfolio" ]
, p [] [ text "Just Another Writer's Portfolio" ]
, navigation
, about
, div [ class "row" ]
[ div [ class "col-md-4 mb-5" ]
[ poem ]
, div [ class "col-md-4 mb-5" ]
[ poem ]
, div [ class "col-md-4 mb-5" ]
[ poem ]
]
, stories
, contact
]
]

Finally, let’s update the Main.elm file too:


main : HTML Never
main =
div [ class "bg-light" ]
[ view ]

Our Elm-powered website should, at this point,


look like this:
We have made a number of updates in the
aforementioned files. Although all of these updates
deal with concepts we have already covered in this
book, writing out the preceding code in our app is a
great way to reinforce what we’ve learned before
moving on to more advanced topics.
In the preceding code, one point of possible
confusion could be the use of various Bootstrap 4
classes. For more information about these classes,
please refer to the official documentation for
Bootstrap 4, or to a number of titles in the Packt
library that cover Bootstrap 4 in detail.
Now that all our files are complete, let’s save
everything and look at the result. There are many
ways in which we could have improved our
Portfolio app. For example, we still need to give it a
working navigation. Unfortunately, at this point of
our journey into Elm, adding navigation to our web
page would require being familiar with a number of
relatively complex topics, which would only create
confusion. That’s why we’ll leave our static page as
it is.
If you would like to experiment with changing things
up in the portfolio for the sake of practice, remember
that you can use the HTML to Elm website at https://mbyls
tra.github.io/HTML-to-elm/, which should make things both

faster and easier.

In the section that follows, rather than prematurely


getting into more advanced concepts to build a
complex navigation solution, we’ll look at a simple
alternative. This alternative will deal with using
case expressions to display different sections of our
static page on the screen. The advantage of this
approach is that we’ll reinforce how all the working
parts of the Elm architecture fit together.
Rendering sections of
our page using case
expressions
We will start this section from scratch. Create a
new folder and run the following command on it:
create-elm-app alternative-to-navigation. Once the app is
ready, navigate to the alternative-to-navigation folder,
open Main.elm in src, and erase all the code. Add the
following code to Main.elm. The following code works
as is, so feel free to save it and run the app:

module Main exposing (main)

import HTML exposing (..)


import HTML.Attributes exposing (..)
import HTML.Events exposing (onClick)

model =
""

type Msg
= FirstButtonMessage
| SecondButtonMessage
| ThirdButtonMessage
| FourthButtonMessage

update msg model =


case msg of
FirstButtonMessage ->
"You've clicked the first button!"

SecondButtonMessage ->
"You've clicked the second button!"

ThirdButtonMessage ->
"You've clicked the third button!"

FourthButtonMessage ->
"You've clicked the fourth button!"
view model =
div []
[ h1 [ class "display-4 mt-5" ] [ text "My Portfolio"
, div [ class "container" ]
[ h2 [ class "strong" ] [ text "Just Another Writ
, hr [] []
, div [ class "mt-5" ]
[ button
[ class "btn btn-primary mr-1", onClick F
[ text "First Button" ]
, button
[ class "btn btn-primary", onClick Second
[ text "Second Button" ]
, button
[ class "btn btn-primary", onClick ThirdB
[ text "3rd Button" ]
, button
[ class "btn btn-primary", onClick Fourth
[ text "4th Button" ]
, p [ class "mt-5 lead" ] [ text model ]
]
]
]

main =
HTML.beginnerProgram
{ model = model
, view = view
, update = update
}

Before we can improve the preceding code, we first


need to make sure that we understand it. As we
previously discussed, the first five lines deal with
importing modules that we will be using in our
application. Next, we set the model to the value of an
empty string.
We follow this up by creating a union type of Msg,
which can equate to only one of four values:
FirstButtonMessage, SecondButtonMessage, and so on. Why
the verbose Msg value names? Because we want to
make it absolutely clear that the Msg union type
holds messages. These messages will be emitted to
the update function once a user clicks one of the
buttons.
Next, we write our update function, which takes a
message and a model.

Reminder: The first parameter of the update function is


the name of the case expression inside the update
function.

Then, we pass it a case expression, which will


execute one of the four pieces of code based on
what message it received. So, if the update function
receives the message which we aptly named
FirstButtonMessage, the case expression will evaluate to
the You've clicked the first button! string.
Next, our view function takes in a model, and we pass
it a div function, which holds an h1 and a div with the
class of container. In turn, this wrapping div holds an
h2, an hr, and another div, which holds four buttons

and a p function.
Each button’s attributes list takes two attributes;
the first one is the class to use on the button, and
the second one is the onClick event. Each onClick event
will emit a different message, ready to be accepted
and used by the update function.
Finally, the main function takes in HTML.beginnerProgram,
which was explained in Chapter 2, Building Your
First Elm App. Now that we understand just what
the preceding code is doing, we can improve it a
bit.
Improving our
messages
To begin this section, let’s look at the completed
code, with all the improvements made to it, and
after that, we’ll discuss the changes made to the
code:

module Main exposing (main)

import HTML exposing (..)


import HTML.Attributes exposing (..)
import HTML.Events exposing (onClick)

model : String
model =
""

type Msg
= FirstButtonMessage
| SecondButtonMessage
| ThirdButtonMessage
| FourthButtonMessage

update : Msg -> a -> String


update msg model =
case msg of
FirstButtonMessage ->
"""
You've clicked the "About Me" button! \n

I'm a successful writer.

"""

SecondButtonMessage ->
"""
You've clicked the "My Poems" button!

I've written over 50 poems in the last 10 years.


"""

ThirdButtonMessage ->
"""
You've clicked the "My Stories" button!

I've written a short story in the sci-fi genre


and it won the Nebula award.

"""

FourthButtonMessage ->
"""
You've clicked the "Contact Me" button!

To get in touch, send me an email.


My email address is: me@example.com

"""

view : String -> HTML Msg


view model =
div []
[ h1 [ class "display-4 mt-5" ] [ text "My Portfolio"
, div [ class "container" ]
[ h2 [ class "strong" ] [ text "Just Another Writ
, hr [] []
, div [ class "mt-5" ]
[ button
[ class "btn btn-primary mr-1", onClick F
[ text "About Me" ]
, button
[ class "btn btn-primary mr-1", onClick S
[ text "My Poems" ]
, button
[ class "btn btn-primary mr-1", onClick T
[ text "My Stories" ]
, button
[ class "btn btn-primary mr-1"
, style [( "cursor", "pointer" )]
, onClick FourthButtonMessage
]
[ text "Contact Me" ]
, p [ class "mt-5 lead" ] [ text model ]
]
]
]

main : Program Never String Msg


main =
HTML.beginnerProgram
{ model = model
, view = view
, update = update
}

In this iteration of our app, the focus was on


improving the messages. Basically, we wanted each
message to have some meaningful text, and in
order to do that, we used multiline strings, which
start and finish with three sets of double quotes.
While that makes it easier for us to see all the text
that was typed, it does not give us a flexible way to
style our strings better, since, as you can see in the
preceding code, multiline quotes in Elm cannot be
broken onto the next line by using the newline
character.
Another change that we made involved adding a
style function on the fourth button so that when a
user hovers over the button, it displays a pointer
instead of the regular cursor. This improvement
makes it more obvious that the button is clickable.
Another improvement that we made was giving
type annotations to each of our functions so that we
can see that the model function takes a String. The
update function takes a message and a model (which can
be anything) and returns a String. The view function
takes a String and returns an HTML Msg, which is our
own custom union type, as discussed before.
With the updated Portfolio app being served in our
browser, upon clicking the first button on the
navigation, the About Me button, we will get the
following output:
Clicking other buttons will swiftly change what we
see on the screen.
If you cannot see the styles, make sure to add the
Bootstrap 4 CDN link to public/index.HTML (just like we did
for the Portfolio app):

<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">


<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com
<title>Elm App</title>
</head>

Next, we’ll improve our portfolio just a bit more, by


adding a type alias of Model.
Adding the type alias of
Model
By adding a type alias for our Model, we’ll make it
easier to change our Model from something other
than String, if we ever decide to do so, which makes
our code more maintainable.
Just under the module imports, at line 8 of our
app, add this snippet of code:
type alias Model =
String

Next, let’s update type annotations throughout the


app:

model : Model
model =
...

update : Msg -> Model -> String


update msg model =
...

view : Model -> HTML Msg


view model =
...

It’s obvious what we are doing in the code: we are


aliasing the value of String with the type alias Model. It is
very important to understand this and commit it to
memory since this simple example shows exactly what
type alias does and how it works.

Making these changes, we can now see an


interesting pattern: the update function’s type
annotation uses the Msg, with the capital M, while we
pass it the msg, with the small m, since it’s the first
argument.
Similarly, we are passing the model with the small
letter m to our view, but in our view function’s type
annotation, we are referencing the Model with the
capital letter M.
What’s going on here? The explanation is simple.
We can think of the lowercase instances as simply
generic labels, which can be anything. For example,
consider the following changes to our code.
First, let’s replace the existing update function’s msg
with this:
update a model =
case a of

Next, let’s replace the existing view function’s model


with a, as follows:

view a =
...
, p [ class "mt-5 lead" ] [ text a ]
]
]
]

The fact that we replaced both msg and model with the
more generic a in the preceding code, and did not
break it, is great. The compiler happily performs its
duties, and we still have a working app.
Type alias is used to make it easier to read complex
type annotations. Also, type annotations are
capitalized by default, and so are union types.
Thus, both the Model and the Msg are capitalized in
our code, and we cannot change them.
Working with List.map
and List.filter
As discussed in previous chapters, Elm works with
immutable data structures. How do we then use the
existing immutable data structure to find its
members that satisfy only a certain condition or to
produce different values based on existing values?
To achieve these two goals, we can use the map and
filter functions. To keep things simple, we’ll look at
the List.map and List.filter functions, although .map
and .filter can also be used with some other data
structures in Elm.
Let’s say our goal is to take a List of numbers and find
only those that are divisible by 3. To begin, let’s
define a function that will take an Int and return a
Boolean (either True or False), based on whether the

number given to the function is divisible by


3. Navigate to elmrepl.cuberoot.in, and type the
following function definition:
findThrees num = num % 3 == 0

Upon pressing the return key, the Elm REPL will


return the following:

<function> : Int -> Bool

Our findThrees function takes an Int and returns a


Boolean. Put differently, the expression num % 3 == 0 is

evaluated first. Let’s say that num is 3, making the


expression look like this: 3 % 3 == 0. This expression
is true, and thus the expression evaluates to the
value of True, which is of type Boolean. Next, this value
is assigned to the findThrees function.
In other words, if we call the findThrees function and
give it number 3 as its parameter, the findThrees
function will return the value of True, which is of
type Boolean.
Next, let’s give our findThrees function to our List.map.
The following code will not work. Try to guess why
before reading the explanation:
List.map findThrees 3

If you ran the preceding code in the REPL, you’d


get the following error:

-- TYPE MISMATCH --------------------------------------------


The 2nd argument to function `map` is causing a mismatch.
3| List.map findThrees 3
^
Function `map` is expecting the 2nd argument to be:
List Int
But it is:
number

Obviously, we can’t give just a number as the


second argument of the List.map function. Instead, to
make this work, we need to give it a List of numbers.
Like this:
List.map findThrees [1,2]

This time, success! REPL returns the following:

[False, False] : List Bool

Let’s try giving it a List of three numbers:


List.map findThrees [1,2,3]
This time, REPL returns:
[False,False,True] : List Bool

Next, let’s type a List of 10 numbers, and store it in


a variable:

ourList = [1,2,3,4,5,6,7,8,9,10]

Running the preceding code in the REPL will


return:
[1,2,3,4,5,6,7,8,9] : List number

Looking at the preceding code, we can say that a


List of numbers is stored in a variable we called
ourList. Now, let’s give the findThrees function to the
List.map function, and pass the ourList as the second
argument:

List.map findThrees ourList

REPL returns a List of Bool values:


[False,False,True,False,False,True,False,False,True,False] :

Finally, let’s try to replace List.map with List.filter:


List.filter findThrees ourList

REPL returns a filter of ourList:


[3,6,9] : List Int
Now that we have practiced using List.map a little bit,
let’s look at its anatomy. List.map takes two
arguments, the first one being a function, and the
second one being the actual List.
The function that is passed as the first argument to
List.map is used to convert the second argument (the

List) to a new List, based on the logic in the


function. List.map does that by running the function
we give it over each single member of the List
provided. This behavior of List.map makes it a great
candidate for improving our FizzBuzz app, which
we will do in the next section.
For now, let’s run a List.map in our Elm-REPL. In
order to be able to run List.map, we need to define a
function it will use. So, let’s open Elm-REPL and
define our custom fizzBuzzer function:
fizzBuzzer number = \
if number % 15 == 0 then \
"fizzBuzz" \
else if number % 5 == 0 then \
"fizz" \
else if number % 3 == 0 then \
"buzz" \
else \
toString number

The REPL gives us back the following:


<function> : Int -> String

Now, we can map over our List with the help of


fizzBuzzer and List.map:

List.map fizzBuzzer [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,

What we get back in the REPL is this:


["1","2","buzz","4","fizz","buzz","7","8","buzz","fizz","11",
: List String

Feel free to play with various combinations of the


building blocks we discussed previously. In the
next section, we will revisit our FizzBuzz app and
apply some of the things we learned in practice.
Revisiting the FizzBuzz
app
We have covered a lot of ground in Chapter 3,
Creating Your Personal Portfolio with Elm. Can
we use our newfound knowledge to make a better
implementation of the FizzBuzz app? Let’s find out!
Let’s begin with the FizzBuzz app as it was in Chapter
2, Building Your First Elm App:

module Main exposing (main)

import HTML exposing (text)

fizzBuzz = "Fizz Buzz"


fizz = "Fizz"
buzz = "Buzz"

fizzBuzzInput value =
if value % 15 == 0 then
fizzBuzz
else if value % 3 == 0 then
fizz
else if value % 5 == 0 then
buzz
else (toString value)

main =
text (fizzBuzzInput 34567)

How can we go about improving the preceding


code?
First, we can implement our FizzBuzz app as a
beginnerProgram. We can also replace the if expressions
with a case expression. Finally, we can introduce
buttons and style everything nicely.
Implementing FizzBuzz
with List.map
Let’s first list out the completed, improved FizzBuzz
app:

module Main exposing (main)

import HTML exposing (HTML, text)

ourList = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]

fizzBuzzCheck fizz buzz fizzBuzz num =


if num % 15 == 0 then
toString fizzBuzz ++ ", "
else if num % 5 == 0 then
toString buzz ++ ", "
else if num % 3 == 0 then
toString fizz ++ ", "
else
(toString num) ++ ", "

main =
text (String.concat (List.map (fizzBuzzCheck "fizz" "buzz" "

Before we start discussing what’s going on in the


preceding code, let’s quickly update the main
function using the forward function application
operator, |>:
module Main exposing (main)

import HTML exposing (HTML, text)

ourList = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]

fizzBuzzCheck fizz buzz fizzBuzz num =


if num % 15 == 0 then
toString fizzBuzz ++ ", "
else if num % 5 == 0 then
toString buzz ++ ", "
else if num % 3 == 0 then
toString fizz ++ ", "
else
(toString num) ++ ", "

main =
List.map (fizzBuzzCheck "fizz" "buzz" "fizz buzz") ourList
|> String.concat
|> text

Seeing the main function written in this different


notation might make it simpler to understand what
is happening in the preceding code. After
importing the Main and HTML modules, we declare the
ourList variable and the fizzBuzzCheck function
definition.
As we can see, the fizzBuzzCheck function takes four
parameters and returns a value of type String.
The main function maps ourList based on the logic in
the fizzBuzzCheck function, then we use the String.concat
function to take that List of Strings that the List.map
produced, and turn it into a single String, because
the text function receives a single String value as its
parameter.
Summary
In this chapter, we have covered a number of
important topics, namely:

Using elm-make-app

Adding Bootstrap 4 styles to our app

Working with the HTML module in Elm, and


understanding function signatures for
HTML elements

Modularizing our app by splitting the views


into multiple files

Using case expressions to render sections of


a web page dynamically

Working with type aliases

Mapping and filtering Lists of values

Improving our FizzBuzz app using List.map


and String.concat

In the next chapter, we will start using Records for


our model and update functions as we begin building
our unit conversion website. We will also learn
about let expressions and how to use them, and we
will wrap everything up by using a third-party Elm
module for the first time in our code.
Preparing a Unit
Conversion Website in
Elm
Welcome to Chapter 4, Preparing a Unit Conversion
Website in Elm. The goal of this chapter is to
prepare a website that will convert miles to metric
system measurements, that is, to kilometers. By
completing this chapter, you will acquire practical
skills that are integral to how Elm works, all in a
fun project.
The topics we will cover include:

Elm language features used to make the


website, which include type annotations,
case expressions, union types, and messages

Understanding Result as a way to handle


errors

Discussing Elm architecture, and workflow


concepts to make the website

After completing this chapter, you will be able to:

Work with type annotations, case


expressions, union types, and messages
What are we going to
build?
In this chapter, we will build the core of a simple
unit conversion website. We will introduce a lot of
new concepts and use them in practice right away.
To be able to fit everything into one chapter, we’ll
only set up the basic plumbing.
In the next chapter, we’ll expand on what we have
built to create a more complex structure. To get
started, we’ll start a brand new Elm app.
To do that, run the following command in your
console:
create-elm-app unit-converter-simple

Remember that to run your app, you need to point


your console to your new app folder, and then use
the elm-app start command in your console. Now that
we’ve set everything up for development, it’s time
to start building the app.
Building our Unit
Conversion app
To begin, let’s delete everything in Main.elm. Next,
let’s set up a working bare-bones app:

module Main exposing (..)

import Html exposing (..)


import Html.Attributes exposing (..)
import Html.Events exposing (..)

-- Model
init =
{}

-- Update
type Msg
= Nothing

update msg model =


model

-- View
view model =
div [] [ text "Everything will go here" ]

-- Main
main =
beginnerProgram
{ model = init
, view = view
, update = update
}

At this point, we have an app that works, that is,


that displays on the screen. However, it doesn’t
really do anything. Still, it is worthwhile to look at
what each line of the preceding code actually does.
We start off the usual way, by declaring the
module and importing all the other modules
main
used in our app.
The init function is our app’s initial model, which
we set to an empty Record. Next, we set up a union
type of Msg, and give it the value of Nothing. Nothing is
simply nothing—our Msg union type currently has no
value.
Next, we pass two parameters to our update
function, msg and model, and return the model. The view
function is just a div with a text node in its second
List.

Finally, let’s look at the entry point of our app, the


main function. We simply pass it the beginnerProgram,
setting the model to the value of init, view to view, and
update to update. Next, let’s update the init function.
Updating the init
function
Our init function will be the initial state of our app.
Let’s update it like this:

init =
{ unit1 = "Kilometers"
, unit2 = "Miles"
, ratio = 1.608
, convertedValue = 0.0
}

The init function gets assigned the value of a Record


that has two Strings and two Floats. Basically, here we
are modeling the data that our app will use.
Type annotations and
type aliases
At this point, we might want to add the type
annotation above the init function. If you are using
the linter with Atom, you should see the following
warning in the editor:

Top-level value 'init' does not have a type annotation.


I inferred the type annotation so you can copy it into your c
init : { convertedValue : Float, ratio : Float, unit1 : Strin

Note that the order of named values inside the type


annotation does not match the order we gave the
Record we assigned to our init function. The reason is

simple: records in Elm are not index-based.


Thus, we can choose to add the suggested type
annotation above the init function, and the
compiler will be happy.
However, there is an even better thing that we can
do: use a type alias instead. With type alias, we
shorten our type annotations, making it easier to
use them wherever needed. In this case, we need to
create a type alias to use on the init function. Since
the init function is basically just the initial model, it
only makes sense to create a type alias called Model,
and then use it as needed.
Thus, let’s set up a type alias Model, just above the
init function in our code, like so:

type alias Model =


{ convertedValue : Float
, ratio : Float
, unit1 : String
, unit2 : String
}

Now, our updated Main.elm will look like this:


module Main exposing (..)

import Html exposing (..)


import Html.Attributes exposing (..)
import Html.Events exposing (..)

-- MODEL

type alias Model =


{ convertedValue : Float
, ratio : Float
, unit1 : String
, unit2 : String
}

init : Model
init =
{ unit1 = "Kilometers"
, unit2 = "Miles"
, ratio = 1.608
, convertedValue = 0.0
}

Looking at the preceding code, we can see that the


type annotation for init is simply init : Model,
because now we are using the type alias of Model.
Let’s turn our attention to the view and update
functions now.
Updating the view and
update functions
It’s important to understand that the view and update
functions are connected via messages. Whatever
message is sent out by the view function is received
by the update function. That’s why the type of
message that the view function sends should be the
same type that the update message receives. This
might seem obvious, but having no doubts about
this will make you a better Elm developer.
First, let’s add some more content to our view
function:
view model =
div []
[ p []
[ label [ for "unit1Input" ] [ text "Kilometers"
, br [] []
, input [ id "unit1Input" ] []
, button [ onClick Nothing ] [ text "Switch to Mi
, p [ id "unit2Value" ] [ text "Result of calcula
]
]

At this point, our view is displaying on the screen,


and the button sends the message of Nothing to update.
If we compile the app and run it at this point, we
will see an improved UI on our page. However,
everything is static, and clicking the button will not
do anything at this point.
Let’s begin making the page interactive by
replacing the Nothing value with a different value.
We’ll call it Swap:
view model =
div []
[ p []
[ label [ for "unit1Input" ] [ text "Kilometers"
, br [] []
, input [ id "unit1Input" ] []
, button [ onClick Swap] [ text "Switch to Miles
, p [ id "unit2Value" ] [ text "Result of calcula
]
]

Since our view function is now sending the Swap


message onClick of the button in our view, let’s update
our union type of Msg, with the value of Swap, just
above the update function:
type Msg
= Swap

Finally, let’s tell the update function what to do with


the message of Swap:

update msg model =


case msg of
Swap ->
{ model | unit1 = model.unit2, unit2 = model.unit

In the preceding code snippet, we are using some


code that we have not seen before. Let’s look at
how it works. If the update function receives a
message of value Swap, it will run the following
expression:

{ model | unit1 = model.unit2, unit2 = model.unit1 }

What does the preceding code do? Let’s start with


this section of code:
{ model | ... }
The preceding code means—return the same model
that you had previously, with the only update
specified in the code on the right-hand side of the
pipe character.
The code on the right of the pipe character does the
following: it sets the value of unit1 to model.unit2, and
the value of unit2 to model.unit1.
If you save your app at this point and run it, you’ll
see that after it renders the HTML in the browser;
nothing else changes. To make sure the code
actually does something, we need to replace the
hard-coded Strings inside the text functions of our
view to the appropriate model values, like this:

view model =
div []
[ div []
[ label [ for "unit1Input" ] [ text model.unit1 ]
, input [ id "unit1Input" ] []
, button [ onClick Swap ] [ text "Switch" ]
, label [ for "unit2" ] [ text model.unit2 ]
, div [ id "unit2Value" ] [ text "1234" ]
]
]

Now, running our code and clicking the button will


actually send the Swap message from the view to the
update, which the user will see as “Kilometers” and

“Miles” on the screen switching places on a button-


click.
Adding the conversion
logic
Now, we can make our app convert the inputs it
receives on a button- click. Let’s begin by updating
our view function so that it displays the initial
converted value, that is, the value of 0.0, as that is
the value we gave the Record that we are passing to
the init function. Note that the following code will
not compile:
view model =
div []
[ div []
[ label [ for "unit1Input" ] [ text model.unit1 ]
, input [ id "unit1Input" ] []
, button [ onClick Swap ] [ text "Switch" ]
, label [ for "unit2" ] [ text model.unit2 ]
, div [ id "unit2Value" ] [ text model.convertedV
]
]

Running our app with the preceding update will


result in the compiler throwing an error. Why?
Simply because the text function must always
receive a String. So, we need to first run the toString
function on model.convertedValue, and then give the
result of that expression to the text function. Like
this:

...
, div [ id "unit2Value" ] [ text (toString model.
]
]

Running the app at this point will only introduce a


minor change on the screen. A zero will be
displayed on the screen. However, if the user types
into the input field, this will not affect the zero—it
will still just sit there. Let’s fix that now by
improving the input function in our view.
Improving the input
function
In order for our app to be able to convert the values
entered into the input text field, we need to be able
to do something with that input. In other words, we
need to send the value of the input to the update
function, and then tell the update function what to do
with the message that it received.
So first, let’s add to our view function so that when
the user types something, it sends a message of that
event to the update function:

view model =
div []
[ div []
[ label [ for "unit1Input" ] [ text model.unit1 ]
, input [ id "unit1Input", onInput Convert ] []
, button [ onClick Swap ] [ text "Switch" ]
, label [ for "unit2" ] [ text model.unit2 ]
, div [ id "unit2Value" ] [ text (toString model.
]
]

In the preceding code, we have just added another


item to the first List of the input function. The item
we added is the onInput function, which receives a
parameter we called Convert.
The stumbling block
So far in the development of our app, we haven’t
had any major issues. We are about to hit a minor
road bump, and it’s important to walk through this
slowly, just as it’s laid out in the section that
follows.
We are done with the view. Now, we can change our
update function accordingly:

update msg model =


case msg of
Swap ->
{ model | unit1 = model.unit2, unit2 = model.unit

Convert newValue ->


{ model | convertedValue = newValue }

If we ran our app right now, it would not compile.


Even though the preceding code is perfectly valid,
there is a piece missing. Have a look at the error
message, and try to understand what the reason is
for this error:

NAMING ERROR
Line 28, Column 9
Cannot find pattern Convert
NAMING ERROR
Line 36, Column 48
Cannot find variable Convert

Obviously, on line 28, our update function is trying to


pattern-match for the pattern called Convert, and it
cannot find it. Similarly, on line 36, our view function
is trying to use a variable called Convert, but it cannot
find it.
To solve this error, let’s briefly think about the
messages that our view function can send. How
many are there?
Of course, there are only two messages: Swap and
Convert.

If you look at the Swap message, you’ll see that it’s


used in three places in our code: in the view
function, as a parameter of the onClick function. In
the update function’s case expression, it is used as the
Swap pattern, and in the union type of Msg, it is used

as its only possible value.


Thus, to solve our error, we need to add the Convert
value as another possible value of the Msg union
type, like this:
type Msg
= Swap
| Convert

Running the app now will result in another error,


but the solution to this one should be a bit more
obvious. Here is the text of the error:
TOO MANY ARGUMENTS
Line 29, Column 9
Pattern Main.Convert has too many arguments.
Expecting 0, but got 1.

As we can see from the error message, the compiler


is looking at the Convert pattern, and it sees that it
has the argument we called newValue. But when it
looks at the Msg union type, it only sees Convert. There
are no arguments there!
To fix the current error, we need to specify the
argument that the Convert value must take along
with it:
type Msg
= Swap
| Convert String

Saving the preceding changes in our app will bring


us one step closer to the fully working code.
With the current code, even after the
improvements, our app still won’t compile. An
impatient reader at this point might start getting
slightly irritated with the compiler, helpful as it is.
However, what follows is probably the most
important piece of knowledge in this entire
chapter. It’s about toggling primitive values in our
apps - a subject that is rarely, if ever, discussed in
online resources, perhaps because it is considered
as another one of those expected understandings.
So, let’s work through this stumbling block. To
begin, let’s revisit our model, that is, the type alias of
Model:

type alias Model =


{ convertedValue : Float
, ratio : Float
, unit1 : String
, unit2 : String
}

As we can see in the preceding code, the


convertedValue is a Float, not a String. We might think
that the solution would be to simply change the
value passed to Convert from String to Float, in our Msg
union type:
type Msg
= Swap
| Convert Float
Unfortunately, this will only result in a different
error:
TYPE MISMATCH
Line 37, Column 48
The argument to function onInput is causing a mismatch.

Function onInput is expecting the argument to be:

String -> msg


But it is:
Float -> Main.Msg

So, obviously, our onInput message needs to be a


String. Let’s try converting the newValue, which is a
String, to a Float:

Convert newValue ->


{ model | convertedValue = String.toFloat newValu

The preceding code will throw yet another error,


and this one might look a bit intimidating at first:
TYPE MISMATCH
Line 47, Column 9
The argument to function beginnerProgram is causing a mismat
Function beginnerProgram is expecting the argument to be:
{ ..., update : Main.Msg -> Main.Model -> Main.Model }
But it is:
{ ...
, update :
Main.Msg
-> { convertedValue : Result.Result String Float
, ratio : Float
, unit1 : String
, unit2 : String
}
-> { convertedValue : Result.Result String Float
, ratio : Float
, unit1 : String
, unit2 : String
}
}
Hint: Problem at update.convertedValue...
The reason why this message might look a bit less
intuitive from what we’re used to is two-fold.
Firstly, the message is reporting an error that has
to do with our type alias Model, which was great at
hiding complexity from our app, but now it might
be hampering our efforts to understand the issue.
Secondly, inside the error, we can see a new Elm
keyword: Result.
Let’s resolve the error step by step. We’ll begin by
trying to produce a more understandable error, by
commenting out the type alias of Model, together
with the init function’s type annotation:
module Main exposing (..)

import Html exposing (..)


import Html.Attributes exposing (..)
import Html.Events exposing (..)

-- Model
{--
type alias Model =
{ convertedValue : Float
, ratio : Float
, unit1 : String
, unit2 : String
}

init : Model
--}

Now, let’s try recompiling the app to get a slightly


different error message, as follows:

TYPE MISMATCH
Line 49, Column 9
The argument to function beginnerProgram is causing a mismat
Function beginnerProgram is expecting the argument to be:
{ ...
, update :
Main.Msg
-> { ..., convertedValue : Float }
-> { ..., convertedValue : Float }
}
But it is:
{ ...
, update :
Main.Msg
-> { ..., convertedValue : Result.Result String Float }
-> { ..., convertedValue : Result.Result String Float }
}
Hint: Problem at update.convertedValue...

The preceding error message makes it slightly more


obvious. We are having issues with this Result.Result
thing. Just what is it?
Dealing with the
Result.Result error
is a type. We use the Result type whenever we
Result

have a function that could possibly return an error.


In our preceding example, we have an input field.
The user types into the input field, and we are
expecting the input to be of type Float. However,
what if the user types in one or more letters, or a
combination of letters and numbers, or any other
odd character other than just numbers?
Conceptually, it boils down to two possibilities—a
function returns an error by virtue of the user
typing unexpected characters into the input field,
or it returns a result, by virtue of the user typing
Floats, as we expect.

This is how the Result package is defined in the


official documentation, available at: http://package.elm
-lang.org/packages/elm-lang/core/latest/Result:

“A Result is the result of a computation that may fail. This is a great


way to manage errors in Elm.”

The official documentation gives the following


union type definition:

type Result error value


= Ok value
| Err error

So, if our function succeeds, we’ll get an Ok with the


value; otherwise, we’ll get an Err with an error. This
begs the question: how do we then deal with errors
in our programs, using Result?
At its core, the solution is simple and elegant, like
everything else in Elm: if we get an Ok, the function
should return a value; if we get an Err, the function
should return a default value. Let’s take another
example from the official documentation and run it
in the REPL:
Result.withDefault 0 (String.toInt "123") == 123

In the preceding code, we are parsing the string of


"123" to an Int, but just to be on the safe side, in case
we get an Err, we set the default value to zero.
This is what the REPL returns:
True : Bool

Let’s try the second example now:

Result.withDefault 0 (String.toInt "abc") == 0

What will REPL return now? The exact same thing:


True : Bool

The conclusion is, no matter what we write inside


the double quotes, Elm will take care of it, as long
as we have a default solution for Err values.
Now that we understand what Result.Result is, let’s go
back to fixing our app.
Using the Result type
to fix our app
After getting sidetracked with a lot of error
messages and some dry theory, let’s put our new
skills in to practice by changing the update function
to the following code:

update msg model =


case msg of
Swap ->
{ model | unit1 = model.unit2, unit2 = model.unit

Convert newValue ->


{ model | convertedValue = Result.withDefault 10

The preceding code should be a lot easier to


understand now. If our update function receives a
message that pattern-matches the Convert pattern
with its newValue string, we’ll execute the code after
the arrow.
The code after the arrow says: use the existing
model, with a change to be made only to the
model’s convertedValue. The updated value to assign to
convertedValue is the result of the expression:

Result.withDefault 10 (String.toFloat newValue)

Elm evaluates the expression starting with the


parentheses: it takes the newValue String and converts
it to Float.
If the operation is successful, it returns the given
Float as the value of the evaluated expression. If the
operation is not successful, it returns number 10,
and that is indeed what you’ll see on the screen if
you type anything other than numbers into the
input field in your running app.
There is only one thing left to do: uncomment the
type alias Model and the init function’s type
annotation. With all the changes we have had, this
is the full code of our app at this point:
module Main exposing (..)

import Html exposing (..)


import Html.Attributes exposing (..)
import Html.Events exposing (..)

-- Model
type alias Model =
{ convertedValue : Float
, ratio : Float
, unit1 : String
, unit2 : String
}

init : Model

init =
{ unit1 = "Kilometers"
, unit2 = "Miles"
, ratio = 1.608
, convertedValue = 0.0
}

-- Update
type Msg
= Swap
| Convert String

update msg model =


case msg of
Swap ->
{ model | unit1 = model.unit2, unit2 = model.unit

Convert newValue ->


{ model | convertedValue = Result.withDefault 10

-- View
view model =
div []
[ div []
[ label [ for "unit1Input" ] [ text model.unit1 ]
, input [ id "unit1Input", onInput Convert ] []
, button [ onClick Swap ] [ text "Switch" ]
, label [ for "unit2" ] [ text model.unit2 ]
, div [ id "unit2Value" ] [ text (toString model.
]
]

-- Main
main =
beginnerProgram
{ model = init
, view = view
, update = update
}

Run the app and test it out.


If you type a number into the input field, you’ll get
that same number on the next line. However, if you
type any other characters inside the input field,
you’ll get number 10 on the next line—a clear sign
that our function’s Result was Err.
In the next section, we’ll make our app actually
convert the values it receives from the input field.
Calculating the
conversion
Let’s update the Convert pattern in the update
function’s case expression. Our new code will look
like this:
Convert newValue ->
{ model | convertedValue = (Result.withDefault 10

After the update, save the app and test the behavior
of the input field. Once you start typing a number,
you’ll see it immediately being converted to its
converted value in kilometers.
However, there is another improvement we need to
make. As our Result.withDefault is now 10, whenever a
user types anything else besides numbers, or even
before they start typing, we see the number 16.08
displayed on the screen.
This is an easy enough fix. Instead of 10, we’ll
simply use zero as the default Result. Update the
code to the following:

Convert newValue ->


{ model | convertedValue = (Result.withDefault 0

Now, the app has the expected behavior.


However, the expression after the -> operator is a
bit unwieldy. We will make our code a bit nicer to
look at, and learn another feature of the Elm
language along the way, by using a let expression.
Refactoring our app
using a let expression
At this point, we are ready to use a let expression to
improve the Convert pattern of our update function’s
case expression.
A let expression in Elm consists of two parts: let
and in. The let part lets us declare variables and
functions that will be used in the in section of the
let expression. It’s important to note that the
variables declared here are scoped to the function
they are used in. The rest of our program is not
aware of it. In other words, these variables are
local, and do not exist in the rest of our program.
Contrast that to functions such as the update or the
view functions, which live in the global scope.

The in section of a let expression should return a


single value as a result of whatever expressions are
placed there.
Let’s look at how we can write our Convert pattern as
a let expression. To begin, let’s look at it again:
Convert newValue ->
{ model | convertedValue = (Result.withDefault 0

Now, let’s think about putting a part of the


preceding code in a variable, scoped to the let
expression. An obvious candidate would be this bit
of code:

(Result.withDefault 0 (String.toFloat newValue))


What name could we give to the preceding snippet
of code?
How about floatValue? It’s a nice, descriptive name,
since we are indeed converting the existing newValue
string to a Float, and giving it the default value of
zero. So, our updated code will look like this:
floatValue =
Result.withDefault 0 (String.toFloat newValue)

Next, let’s rewrite our Convert pattern as a let


expression:
Convert newValue ->
let
floatValue =
Result.withDefault 0 (String.toFloat newValue)
in
{ model | convertedValue = floatValue * model.ratio }

There, much better.


We’ve reduced the cognitive load of trying to
decipher that long, one-line expression we
previously had, into a nice let expression that is a
lot easier to reason about. Working with let
expressions will soon feel so natural to you that
you’ll wonder how you ever did without them.
Why? Because writing our code like this makes a
clear separation between variables being declared
in the let part of the let expression, and the actual
expression, in its in part.
Making our app look
nicer
In this chapter, we’ve covered a lot of theory and
we’ve also put it into practice. Let’s look at the full
code of our app at this stage:
module Main exposing (..)

import Html exposing (beginnerProgram, div, button, input, te


import Html.Attributes exposing (for, id, value)
import Html.Events exposing (onClick, onInput)

type alias Model =


{ unit1 : String
, unit2 : String
, ratio : Float
, convertedValue : Float
}

initModel : Model
initModel =
{ unit1 = "Kilometers"
, unit2 = "Miles"
, ratio = 1.608
, convertedValue = 0.0
}

main =
beginnerProgram { model = initModel, view = view, update

view model =
div []
[ div []
[ label [ for "unit1Input" ] [ text model.unit1 ]
, input [ id "unit1Input", onInput Convert ] []
, button [ onClick Swap ] [ text "Switch" ]
, label [ for "unit2" ] [ text model.unit2 ]
, div [ id "unit2Value" ] [ text (toString model.
]
]

type Msg
= Swap
| Convert String

update msg model =


case msg of
Swap ->
{ model | unit1 = model.unit2, unit2 = model.unit

Convert newValue ->


let
floatValue =
Result.withDefault 0 (String.toFloat newV
in
{ model | convertedValue = floatValue * model

Next, we’ll focus on making the app look nicer by


using Bootstrap 4.
Adding Bootstrap
styles
To begin, navigate to index.html in your project’s
public folder and add the following line of code:

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/

The starting point of our code update will be a


simple snippet of HTML, copied from the official
Bootstrap documentation website. The code we will
be using is for a Bootstrap-based input group:
<div class="col-lg-offset-3 col-lg-6">
<div class="input-group">
<span class="input-group-btn">
<button class="btn btn-secondary" type="button">Hate
</span>
<input type="text" class="form-control" placeholder="Pr
<span class="input-group-btn">
<button class="btn btn-secondary" type="button">Love
</span>
</div>
</div>

Let’s convert the preceding snippet to Elm code,


using the HTML to Elm page, available at: https://mb
ylstra.github.io/html-to-elm/. After our HTML gets
parsed, this is what we get:
div [ class "col-lg-offset-3 col-lg-6" ]
[ div [ class "input-group" ]
[ span [ class "input-group-btn" ]
[ button [ class "btn btn-secondary", type_ "butt
[ text "Hate it" ]
]
, input [ class "form-control", placeholder "Product
[]
, span [ class "input-group-btn" ]
[ button [ class "btn btn-secondary", type_ "butt
[ text "Love it" ]
]
]
]

Now, we need to map our existing Elm code to the


preceding Elm code. To make it easier to work
with, we will also assign it to a variable. After we’ve
done that, our view function will look like this:
view model =
div []
[ div [ class "col-lg-offset-3 col-lg-6 mt5 pt5" ]
[ h1 []
[ text "Unit Converter App" ]
, div [ class "input-group" ]
[ span [ class "input-group-btn" ]
[ button [ class "btn btn-secondary", type_ "button" ]
[ text model.unit1 ]
]
, input [ onInput Convert, class "form-control", placeholder
, span [ class "input-group-btn" ]
[ button [ onClick Swap, class "btn btn-primary", type_ "but
]
]
, div [ class "mt5 pt5" ] [ text model.unit2 ]
, div [ id "unit2Value" ] [ text (toString model.convertedVa
]
]

The preceding code gives a slightly different HTML


structure than what we had so far, but it is also
more semantic and nicer looking for the end user.
Let’s look at the complete code for our app at this
point:

module Main exposing (..)

import Html exposing (beginnerProgram, div, button, input, te


import Html.Attributes exposing (for, id, value, class, place
import Html.Events exposing (onClick, onInput)

type alias Model =


{ unit1 : String
, unit2 : String
, ratio : Float
, convertedValue : Float
}

initModel : Model
initModel =
{ unit1 = "Kilometers"
, unit2 = "Miles"
, ratio = 1.608
, convertedValue = 0.0
}

main =
beginnerProgram { model = initModel, view = view, update

view model =
div []
[ div [ class "col-lg-offset-3 col-lg-6 mt5 pt5" ]
[ h1 []
[ text "Unit Converter App" ]
, div [ class "input-group" ]
[ span [ class "input-group-btn" ]
[ button [ class "btn btn-secondary", type_ "button" ]
[ text model.unit1 ]
]
, input [ onInput Convert, class "form-control", placeholder
, span [ class "input-group-btn" ]
[ button [ onClick Swap, class "btn btn-primary", type_ "but
]
]
, div [ class "mt5 pt5" ] [ text model.unit2 ]
, div [ id "unit2Value" ] [ text (toString model.convertedVa
]
]

type Msg
= Swap
| Convert String

update msg model =


case msg of
Swap ->
{ model | unit1 = model.unit2, unit2 = model.unit
Convert newValue ->
let
floatValue =
Result.withDefault 0 (String.toFloat newV
in
{ model | convertedValue = floatValue * model

At this point, our app should look like this:

There are a few improvements that can be made to


our app at this point. For example, the Switch
button only switches the names of units between
kilometers and miles, but when the Switch button
is pressed, it does not update the values that are
already present in the input field. Also, the page
layout and styling, although better than what we
had, could still use a bit more improvement.
In the next chapter, we will add these features and
further improve the app so that it has multiple
inputs for the conversion of multiple units. Now,
we’ll shift our focus to looking at ways to apply
what we learned in this chapter and update our
FizzBuzz app.
Revisiting the FizzBuzz
app
Before we can start improving our FizzBuzz app,
let’s remind ourselves of where we left off in the
previous chapter:
module Main exposing (main)

import Html exposing (Html, text)

ourList = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]

fizzBuzzCheck fizz buzz fizzBuzz num =


if num % 15 == 0 then
toString fizzBuzz
else if num % 5 == 0 then
toString buzz
else
toString num

main =
List.map (fizzBuzzCheck "fizz" "buzz" "fizz buzz") ourLis
|> String.concat
|> text

Let’s convert the preceding app so that it works


with the Elm architecture, and so it prints out
either a number or a word based on user input.
We’ll begin with the bare-bones app we used
before, which utilizes the beginnerProgram function:
module Main exposing (..)

import Html exposing (..)


import Html.Attributes exposing (..)
import Html.Events exposing (..)

-- Model
initModel =
{}

-- Update
type Msg
= Nothing

update msg model =


model

-- View
view model =
div [] [ text "Everything will go here" ]

-- Main
main =
beginnerProgram
{ model = initModel
, view = view
, update = update
}

Next, let’s populate our initial model:


initModel =
{ inputValue = ""
, outputValue = 0.0
}

Now, let’s add a type alias of Model, and update the


type annotation of initModel accordingly:
type alias Model =
{ inputValue : String
, outputValue : Float
}

initModel : Model
initModel =
{ inputValue = ""
, outputValue = 0.0
}

Next, let’s give our view function some HTML:


-- View
view model =
div []
[ div [ class "col-lg-6" ]
[ div [ class "input-group" ]
[ input
[ onInput DisplayInput, class "form-contr
[]
, span [ class "input-group-btn" ]
[ button [ class "btn btn-secondary", typ
[ text "FizzBuzz It!" ]
]
]
, div [ class "display-4" ] [ text (toString mode
]
]

If we compile the app now, we’ll see an input and a


button in our browser. What we want to do in this
version of the FizzBuzz app is we want the user to
type a number inside the input, and after they click
the button, the app will print out either a number
or one of the words, as per the rules of the game.
Now, we can start to make the app receive user
input. Let’s update the nested input function inside
the view function by adding an onInput message:

[ input [ onInput DisplayInput, class "form-control", placeho

Now, we need to allow our update function to receive


that message:
update msg model =
case msg of
DisplayInput newValue->
{ model | outputValue = Result.withDefault 0 (Stri

Of course, we still need to make changes to the Msg


union type:
type Msg
= DisplayInput String

If we ran our app now, we’d see an input, a button,


and below it, the number zero. If we typed letters,
nothing would change; however, if we typed
numbers, they would get displayed in place of the
zero.
Giving our FizzBuzz
app some logic
In this section, we’ll give our FizzBuzz app some
logic so that it displays results based on user input.
Let’s do that by adding logic to the DisplayInput patter
of our update function’s case expression:

update msg model =


case msg of
DisplayInput newValue ->
let
condition =
if (Result.withDefault 1 (String.toInt newV
"fizzBuzz"
else if (Result.withDefault 1 (String.toInt
"buzz"
else if (Result.withDefault 1 (String.toInt
"fizz"
else
newValue
in
{ model | outputValue = condition }

What we have done in the preceding code can be


explained in several points:

We have given our DisplayInput pattern a


scoped variable we called condition

The condition variable will evaluate to one of


the if expressions inside of it, based on the
number that the user typed into the input
field (which was stored in the newValue
variable)
In the in part of the let expression, we
simply return the same model plus the
updated outputValue, based on the value that
the condition variable equated to in the let
part of the let expression

We can now save and run our app and observe how
it dynamically updates the text node of the div
under the input field, based on user inputs.
We can observe the following behavior:

Typing a number will result in a correct


calculation of a number or a word from the
FizzBuzz game

Typing anything else will return that exact


same string in the div under the input

This means that there is still more room for


improvement for our app. We will improve it by
simply adding another if expression to our
condition variable, and resolving the rest to a
message to the user telling them that they need to
input a number, not other characters.
The update we need to make is easy enough:

update msg model =


case msg of
DisplayInput newValue ->
let
condition =
if (Result.withDefault 1 (String.toInt newV
"fizzBuzz"
else if (Result.withDefault 1 (String.toInt
"buzz"
else if (Result.withDefault 1 (String.toInt
"fizz"
else if (Result.withDefault 0 (String.toInt
newValue
else
"Type a number, please!"
in
{ model | outputValue = condition }

At this point, our FizzBuzz app is behaving a lot


nicer. Let’s see the full code of the app at this point:

module Main exposing (..)

import Html exposing (..)


import Html.Attributes exposing (..)
import Html.Events exposing (..)

type alias Model =


{ inputValue : String
, outputValue : String
}

initModel : Model
initModel =
{ inputValue = ""
, outputValue = ""
}

-- Update
type Msg
= DisplayInput String

update msg model =


case msg of
DisplayInput newValue ->
let
condition =
if (Result.withDefault 1 (String.toInt newV
"fizzBuzz"
else if (Result.withDefault 1 (String.toInt
"buzz"
else if (Result.withDefault 1 (String.toInt
"fizz"
else if (Result.withDefault 0 (String.toInt
newValue
else
"Type a number, please!"
in
{ model | outputValue = condition }
-- View
view model =
div []
[ div [ class "col-lg-6" ]
[ div [ class "pt-5 pb-5 display-4" ] [text "Fizz
, div [ class "input-group" ]
[ input
[ onInput DisplayInput, class "form-contr
[]
, span [ class "input-group-btn" ]
[ button [ class "btn btn-secondary", typ
[ text "FizzBuzz It!" ]
]
]
, div [ class "display-4" ] [ text (toString mode
]
]

-- Main
main =
beginnerProgram
{ model = initModel
, view = view
, update = update
}

This is the welcome screen of our improved


FizzBuzz app:

Typing a number produces the desired result:


Typing anything else will produce a user-friendly
error message:

There are still many things that we can improve, as


we’ll see in Chapter 5, Completing the Unit
Conversion Website in Elm.
Summary
In Chapter 4, Preparing a Unit Conversion Website
in Elm, we learned a number of important
concepts, such as:

Working with the Msg union type

Setting up our model’s data with Records

Using type annotations and type aliases

Updating only a section of our model using


pipe characters in our expressions

Working with the Result union type to handle


potential errors in our apps

Working with let expressions

In the next chapter, we will improve our unit


conversion website by adding multiple inputs for
converting various units.
Completing the Unit
Conversion Website in
Elm
Welcome to Chapter 5, Completing the Unit
Conversion Website in Elm. The goal of this
chapter is to complete the unit conversion website
while discussing the following concepts:

Using Html.map

Working with complex relationships


between different views

Working with multiple models

Combining everything we learned in a


relatively complex app

After completing this chapter, you will be able to:

Build Elm applications that have multiple


models separated into modules, while
following best practices

We’ll begin this chapter by looking at the


completed functionality for our unit converter app.
The reason for this is to be able to see the bigger
picture and understand what is going on in the two
modules of our updated app before continuing to
improve the app itself.
Improving the Unit
Converter app
In this iteration of our app, we have two separate
files that work together to produce our unit
conversion app. At this point, there are no styles in
our app at all, since the focus is on
functionality. Now, let’s rebuild our unit converter
app from scratch.
We’ll start with the barebones Elm app:
module Main exposing (..)

import Html exposing (..)


import Html.Attributes exposing (..)
import Html.Events exposing (..)

-- Model
initModel =
{}

-- Update
type Msg
= Nothing

update msg model =


model

-- View
view model =
div [] [ text "Everything will go here" ]

-- Main
main =
beginnerProgram
{ model = initModel
, view = view
, update = update
}
Now, we will improve it so that it can take multiple
input fields. The first improvement has to do with
separating the current unit converter into a
different module.
Separating
UnitConverter into a
different module
Create a new file, call it UnitConverter.elm, and save it
inside the src folder. Next, copy and paste the
barebones app there as well.
Back in Main.elm, change its imports section so that it
looks like this:
module Main exposing (..)

import Html exposing (beginnerProgram, div, button, input, te


import UnitConverter exposing (..)

Inside UnitConverter.elm, change the imports section so


that it looks like this:
module UnitConverter exposing (..)

import Html exposing (beginnerProgram, div, button, input, te


import Html.Attributes exposing (for, id, value)
import Html.Events exposing (onClick, onInput)

Still in UnitConverter.elm, add the following code:


type alias Model =
{ unit1 : String
, unit2 : String
, ratio : Float
, convertedValue : Float
}

init : String -> String -> Float -> Model


init unit1 unit2 ratio =
Model unit1 unit2 ratio ratio
view model =
div []
[ div []
[ label [ for "unit1Input" ] [ text model.unit1 ]
, input [ id "unit1Input", onInput Convert ] []
, button [ onClick Swap ] [ text "Switch" ]
, label [ for "unit2" ] [ text model.unit2 ]
, div [ id "unit2Value" ] [ text (toString model.
]
]

type Msg
= Swap
| Convert String

update msg model =


case msg of
Swap ->
{ model | unit1 = model.unit2, unit2 = model.unit

Convert newValue ->


let
floatValue =
Result.withDefault 1 (String.toFloat newV
in
{ model | convertedValue = floatValue * model

Notice that the preceding code looks almost


identical to what we had at the end of Chapter 4,
Preparing a Unit Conversion Website in Elm,
except that there is no call to main anymore, and the
init function has changed.

Now, we can update the Main.elm file so that it works


with multiple input fields.
Adding type alias
Model, initModel, and
Main
We’ll start updating Main.elm by adding a new type
alias Model. Place the type alias Model just above the
initModel. The code should be as follows:

type alias Model =


{ lengthConverter : UnitConverter.Model
, weightConverter : UnitConverter.Model
}

Next, let’s update the initModel itself:


initModel : Model
initModel =
{ lengthConverter = UnitConverter.init "Miles" "Kilometer
, weightConverter = UnitConverter.init "Kilograms" "Pound
}

Let’s discuss the preceding code.


Our type alias Model consists of a lengthConverter and a
weightConverter, both of which take the value of
UnitConverter.Model. Looking at type alias

UnitConverter.Model, we see the following:

type alias Model =


{ unit1 : String
, unit2 : String
, ratio : Float
, convertedValue : Float
}
In the initModel function, we have the lengthConverter
take the value from UnitConverter.init, with three
arguments passed to it: “Miles”, “Kilometers”, and
1.608, which is in accordance with the type
annotation of the init function in the UnitConverter
module:
init : String -> String -> Float -> Model

Similarly, we set up the values of initialModel for the


weightConverter, populating it with the following
expression:
UnitConverter.init "Kilograms" "Pounds" 2.2046

Continuing with code in Main.elm, we can add it to


the main function, which has not changed:
main =
beginnerProgram { model = initModel, view = view, update

If we run our app now, we’ll get the Everything will go


here text in the browser. This means that everything
is working and ready for us to improve it.
In the next section, we’ll update the view function.
Updating the Main.view
function
To update the view function, we’ll use the following
code:

view model =
div []
[ UnitConverter.view model.lengthConverter |> Html.ma
, UnitConverter.view model.weightConverter |> Html.ma
]

Let’s look at a compiler-suggested type annotation


for the Main.view function:

view :
{ c
| lengthConverter : { b | convertedValue : a, unit1 :
, weightConverter : { b1 | convertedValue : a1, unit1
}
-> Html.Html Msg

The view function takes a record. The values of the


record will be that of the current model, plus the
update made to lengthConverter or weightConverter. The
lengthConverter function will return either its current
value, signified by type variable b, or the updated
value which sets the convertedValue, unit1, and unit2 to
their respective values. The same goes for
weightConverter—it will return either as is, or its

updated values for convertedValue, unit1, and unit2.


So, the view function takes a record and returns an
Html.Html Msg. Why is the Html module listed here?
Because we have not exposed it explicitly when
importing the Html module. This way of referencing
it is known as the fully qualified style. Had we
exposed the Html function explicitly in the Html
import expression at the top of our Main.elm, we
could have just used -> Html Msg, which is referred to
as an unqualified style.
If we run our app now, the compiler will throw
these two errors:
Cannot find variable LengthConverterMsg
Cannot find variable WeightConverterMsg

Of course it cannot. We have not set it anywhere.


Let’s start doing that. We’ll begin by adding the
union type Msg:

type Msg
= LengthConverterMsg UnitConverter.Msg
| WeightConverterMsg UnitConverter.Msg

The type Msg in Main.elm can be either a LengthConverterMsg


type constructor function, taking the UnitConverter.Msg
as its argument, or it can be a WeightConverterMsg type
constructor function, also taking the UnitConverter.Msg.
Both of these type constructor functions take in the
same value as its single argument. Why is that?
The answer is easy: if you look at the
UnitConverter.update function, you’ll see that if its Swap
pattern gets matched, the expression that evaluates
will do exactly that, swap the values, just as it did in
Chapter 4, Preparing a Unit Conversion Website in
Elm.
If the UnitConverter.update function’s Convert pattern gets
matched, it will return the model as it received it,
plus the updated convertedValue. Again, this is the
same as what we had in Chapter 4, Preparing a Unit
Conversion Website in Elm, which means that the
logic is the same.
What changed then? What changed is the view
function in Main.elm. Let’s pick it apart.
Our Main.view is just a div function. The first List we
pass to it is empty. The second List has two
parameters. The first one looks like this:

UnitConverter.view model.lengthConverter |> Html.map LengthCo

This is the piping syntax that we already discussed.


Another way to write it is to use parentheses, like
this:

Html.map LengthConverterMsg (UnitConverter.view model.lengthC

Let’s start with the expression inside parentheses:


UnitConverter.view model.lengthConverter

Remember that on line 22 of UnitConverter.elm, we can


see that its view function takes a single parameter:
view model =
div []
[ div []
...

Thus, the parameter model.lengthConverter we pass to


UnitConverter.view inside Main.elm is, in fact, the model,
more specifically, the lengthConverter flavor of the
model.
Where does lengthConverter come from? We can find
the answer if we look at the flow of data in Main.elm.
First, the Main.elm entry point, the main function,
receives the value that’s computed by evaluating
the following expression:
beginnerProgram
{ model = initModel
, view = view
, update = update
}

We need to focus on the first line of the preceding


Record: model = initModel. It means that model will get
populated with the initModel value:
{ lengthConverter = UnitConverter.init "Miles" "Kilometers" 1
, weightConverter = UnitConverter.init "Kilograms" "Pounds" 2
}

This record will get stored in our Main.model. Next, the


Main.view function takes the values from the model
and gives them to the div function:
div []
[ UnitConverter.view model.lengthConverter |> Html.ma
, UnitConverter.view model.weightConverter |> Html.ma
]

We ended up where we began, but now we know


that on the first run, our app will give our
UnitConverter.view the model.lengthConverter parameter
that has the following initial values: “Miles”,
“Kilometers”, and 1.608.
Similarly, our app will give our UnitConverter.view the
model.weightConverter parameter that has the following
initial values: “Kilograms”, “Pounds”, and 2.2046.
But that’s only the first part of the rendering
process. What happens next is this bit of code:
Html.map LenghtConverterMsg, and Html.map WeightConverterMsg,
respectively.
Let’s look at the Html.map function now.
Understanding
Html.map
Understanding Html.map is simple. Let’s start with the
official documentation, available at http://package.elm-
lang.org/packages/elm-lang/html/latest/Html#map. The

documentation says that Html.map is used to:


“Transform the messages produced by some Html.”

That’s all it does!


Now, why do we need to transform messages
produced by UnitConverter.view model.lengthConverter in
our Main.elm file? Because in order to use the Html
Msg that gets returned from the UnitConverter.view
model.lengthConverter expression, we need to match it
to the LengthConverterMsg type that the Main.update
function will be using.
In the next section, we’ll add the update function to
our app and then remove the Html.map from
the Main.view function to see what kind of error will
be thrown by the compiler.
Updating the
Main.update function
To start off, let’s add the following code to our
Main.update function:

update msg model =


case msg of
LengthConverterMsg msg_ ->
let
newLengthConverter =
UnitConverter.update msg_ model.lengthCon
in
{ model | lengthConverter = newLengthConverte

WeightConverterMsg msg_ ->


let
newWeightConverter =
UnitConverter.update msg_ model.weightCon
in
{ model | weightConverter = newWeightConverte

Contrary to what you might think the first time you


look at the preceding code, there is not a lot going
on there. As usual, our update function takes a
message and a model. The message comes from
the Model.view function, and it can either be
LengthConverterMsg or WeightConverterMsg, as determined in
the Msg union type above the update function.
As mentioned in Chapter 4, Preparing a Unit
Conversion Website in Elm, for the case expression
to compile successfully, it needs to cover all
possible options, and LengthConverterMsg and
WeightConverterMsg patterns do just that.

Going into a LenghtConverterMsg pattern, we can see


that it takes a parameter we called msg_. This name
was given in order to distinguish between the first
argument passed to the update function, which is just
called msg (without the underscore).
Inside the LenghtConverterMsg pattern, we have a let
expression, and inside of its let part, we specify a
locally-scoped newLengthConverter, which gets the value
evaluated from the following expression:
UnitConverter.update msg_ model.lenghtConverter

Then, in the let-expression’s in part, we return this:


{ model | lengthConverter = newLengthConverter }

The preceding code is straightforward: the


lenghtConverter gets updated with the value returned

from the computation of the expression saved in


newLengthConverter.

In case our Main.update function’s case statement


pattern matches the WeightConverterMsg pattern, the
operations performed are almost identical—the
only difference is that model.weightConverter is being
passed as the second argument to
the UnitConverter.update function.
To conclude this section, let’s look at the Main.update
function’s type annotation, as suggested by the
compiler:
update :
Msg
->
{ c
| lengthConverter :
{ b1
| convertedValue : Float
, ratio : Float
, unit1 : a1
, unit2 : a1
}
, weightConverter :
{ b
| convertedValue : Float
, ratio : Float
, unit1 : a
, unit2 : a
}
}
->
{ c
| lengthConverter :
{ b1
| convertedValue : Float
, ratio : Float
, unit1 : a1
, unit2 : a1
}
, weightConverter :
{ b
| convertedValue : Float
, ratio : Float
, unit1 : a
, unit2 : a
}
}

The update function takes a Msg and a Record and


returns a Record.
If we save all the files and run the app at this point,
we’d get a fully functional unit converter. However,
we still need to remove the Html.map from the Main.view
function, as announced a few pages back, since we
want to see what kind of error the compiler will
throw.
Playing with error messages in Elm is a great way to
learn more advanced concepts, or reinforce concepts
that are somewhat familiar.
Learning from compiler
error messages
Let’s start by removing a single Html.map function
from Main.view, so that its updated code looks like
this:
view model =
div []
[ UnitConverter.view model.lengthConverter
, UnitConverter.view model.weightConverter |> Html.ma
]

Make sure to remove the type annotation as well, to


avoid additional error messages. Saving and
running the app after these changes will produce
the following error message:

The 1st and 2nd entries in this list are different types of v

The 1st entry has this type:

Html.Html UnitConverter.Msg

But the 2nd is:

Html.Html Main.Msg

Hint: Every entry in a list needs to be the same type of valu


never run into unexpected values partway through. To mix diff
single list, create a "union type" as described in:
<http://guide.elm-lang.org/types/union_types.html>

As we discussed previously, the first entry, since it’s


not being converted to lengthConverterMsg via Html.map,
has the type of Html UnitConverter.Msg. Looking at
the Msg union type of the UnitConverter type, we can see
that it can be either Swap or Convert String.
However, looking at Main’s Msg union type, we can
see that it can be either LengthConverterMsg
UnitConverter.Msg, or WeightConverterMsg UnitConverter.Msg. In
other words, it can be either of these two function
constructors, taking UnitConverter.Msg as its
parameter.
To drive the point home, let’s erase the other piped
function from Main.view as well. Doing that will
produce the following error message:
The argument to function `beginnerProgram` is causing a misma

Function `beginnerProgram` is expecting the argument to be:

{ ..., view : Main.Model -> Html.Html Main.Msg }

But it is:

{ ...
, view :
{ lengthConverter : UnitConverter.Model
, weightConverter : UnitConverter.Model
}
-> Html.Html UnitConverter.Msg
}

This concludes the changes made to our code’s


logic. However, there is still quite a lot of work to
be done. We will make our app look a lot nicer by
using a free Bootstrap 4 template as our starting
point and then adjusting it to our needs.
A Bootstrap-based
HTML layout for our
app
Let’s look at the completed layout for our app. The
layout is based on the Cover template provided by
the official Bootstrap docs, and has several
sections. We’ll cover them in the order in which
they appear in the code, top to bottom.
To begin with, we have the DOCTYPE and the head tag.
This code sets the Bootstrap 4 template as an
HTML5 page, provides some meta information,
gives our page a title, and calls the Bootstrap 4 CSS
from a CDN (content delivery network):
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-s
<meta name="description" content="">
<meta name="author" content="">

<title>Cover Template for Bootstrap</title>

<!-- Bootstrap core CSS -->


<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com

Just above the closing head tag, we have a relatively


lengthy style tag. This tag provides custom CSS
styles on top of the regular Bootstrap 4 styles we
imported from a CDN, as explained in the
preceding code. The custom styles have the
following code:
<!-- Custom styles for this template -->
<style>
a,
a:focus,
a:hover {
color: #fff;
}
html,
body {
height: 100%;
background-color: powderblue;
}
body {
/*color: #3a7f3d;*/
text-align: center;
}

/* Extra markup and styles for table-esque vertical and hori


.site-wrapper {
display: table;
width: 100%;
height: 100%; /* For at least Firefox */
min-height: 100%;
}
.site-wrapper-inner {
display: table-cell;
vertical-align: top;
}
.cover-container {
margin-right: auto;
margin-left: auto;
}

/* Padding for spacing */


.inner {
padding: 2rem;
}

/*
* Header
*/
.masthead {
margin-bottom: 2rem;
}

.masthead-brand {
margin-bottom: 0;
}

.nav-masthead .nav-link {
padding: .25rem 0;
font-weight: 700;
background-color: transparent;
border-bottom: .25rem solid transparent;
}

.nav-masthead .nav-link:hover,
.nav-masthead .nav-link:focus {
border-bottom-color: rgba(255, 255, 255, .25);
}

.nav-masthead .nav-link + .nav-link {


margin-left: 1rem;
}

.nav-masthead .active {
color: #111;
border-bottom-color: #fff;
}

@media (min-width: 48em) {


.masthead-brand {
float: left;
}
.nav-masthead {
float: right;
}
}

/*
* Cover
*/
.cover {
padding: 0 1.5rem;
}
.cover .btn-lg {
padding: .75rem 1.25rem;
font-weight: 700;
}

/*
* Footer
*/
.mastfoot {
color: rgba(255, 255, 255, .5);
}
/*
* Affix and center
*/
@media (min-width: 40em) {
/* Pull out the header and footer */
.masthead {
position: fixed;
top: 0;
}
.mastfoot {
position: fixed;
bottom: 0;
}

/* Start the vertical centering */


.site-wrapper-inner {
vertical-align: middle;
}

/* Handle the widths */


.masthead,
.mastfoot,
.cover-container {
width: 100%; /* Must be percentage or pixels for horizontal
}
}

@media (min-width: 62em) {


.masthead,
.mastfoot,
.cover-container {
width: 42rem;
}
}
</style>

</head>

Next, we have the body tag, which holds the page


title and the input fields for our app:
<body>

<div class="site-wrapper">

<div class="site-wrapper-inner">
<div class="cover-container">

<header class="masthead clearfix">


<div class="inner">
<h3 class="masthead-brand">Unit Converter</h3>
<nav class="nav nav-masthead">
<a class="nav-link text-secondary" href="#">Home</a>
<a class="nav-link text-secondary" href="#">Features</a>
<a class="nav-link text-secondary" href="#">Contact</a>
</nav>
</div>
</header>

<div class="inner cover">


<h1 class="cover-heading">Distance converter</h1>
<div class="col-lg-12 mb-5">
<div class="input-group mb-3">
<div class="input-group-prepend">
<button class="btn btn-secondary" type="button">Kilometers:<
</div>
<input type="text" class="form-control" placeholder="" aria-
<div class="input-group-append">
<button class="btn btn-secondary" type="button">Miles:</butt
<button class="btn btn-secondary" type="button">0.0</button>
</div>
</div>
</div>
</div>

<div class="inner cover">


<h1 class="cover-heading">Weight converter</h1>
<div class="col-lg-offset-12">
<div class="input-group mb-3">
<div class="input-group-prepend">
<button class="btn btn-secondary" type="button">Kilograms:</
</div>
<input type="text" class="form-control" placeholder="" aria-
<div class="input-group-append">
<button class="btn btn-secondary" type="button">Pounds:</but
<button class="btn btn-secondary" type="button">0.0</button>
</div>
</div>
</div>
</div>

<footer class="mastfoot">
<div class="inner text-secondary">
<p>Cover template for <a href="https://getbootstrap.com/">Bo
</div>
</footer>
</div>

</div>

</div>

<!-- Bootstrap core JavaScript


================================================== -->
<!-- Placed at the end of the document so the pages load fas
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.j
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.j
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0
</body>
</html>

When we put all of the preceding sections together


and save them as an HTML file, then run that file
in a browser, the browser will render a page that
looks like this:
Now that we have a nice layout to work with, let’s
convert it into an Elm view function.
Converting an HTML
layout into an Elm view
Now that we’ve completed the previous section of
this chapter, it’s time to take a break from Unit
Converter App’s functionality, and look at our app
from an entirely different angle: design.
In order to fully understand the process of creating
a design for our app, we’ll take a drastic approach:
we’ll build a new app from scratch, this time
focusing on design only. After we’ve finished our
design, we’ll add the changes to our Unit
Converter’s functionality.
To be able to follow along, you should create a
completely new app. Let’s call it design-focused.
Point your console to a folder in which you want to
save your design-focused app, and run the
following commands:

create-elm-app design-focused;

cd design-focused;

elm-app start

As we have seen so many times before, this will run


a brand new Elm app, displaying only a navy blue-
colored Elm logo at the upper-center section of our
browser window.
Now, we can start building our app’s design. To
begin, we’ll remind ourselves of the bare-bones
template we used in the previous chapter:

module Main exposing (..)


import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)

-- Model
initModel =
{}

-- Update
type Msg
= Nothing

update msg model =


model

-- View
view model =
div [] [ text "Everything will go here" ]

-- Main
main =
beginnerProgram
{ model = initModel
, view = view
, update = update
}

Now that we have our bare-bones app, it’s time to


add custom styles to it. In our HTML template, we
served them from the head of the HTML
document. This time, we’ll do something different.
Just under the imports in the bare-bones app, add
the following function:
bootstrapCss =
let
tag =
"link"

attrs =
[ attribute "rel" "stylesheet"
, attribute "property" "stylesheet"
, attribute "href" "//maxcdn.bootstrapcdn.com/boo
]

children =
[]
in
node tag attrs children

What this does is create a style tag that calls the


minified Bootstrap CSS file from a CDN.
Next, we’ll update our app’s view function so that its
code is the same as the one we have in our HTML
layout. Once converted from HTML to Elm (using
the online converter at https://mbylstra.github.io/html-to
-elm/), we’ll just add the stylesheet function, as
follows. The layout’s HTML, in the form of an Elm
view function, will look like this:

view model =
div [ class "site-wrapper" ]
[ bootstrapCss
, div [ class "site-wrapper-inner" ]
[ div [ class "cover-container" ]
[ header [ class "masthead clearfix" ]
[ div [ class "inner" ]
[ h3 [ class "masthead-brand" ]
[ text "Unit Converter" ]
, nav [ class "nav nav-masthead" ]
[ a [ class "nav-link text-secondary"
[ text "Home" ]
, a [ class "nav-link text-secondary"
[ text "Features" ]
, a [ class "nav-link text-secondary"
[ text "Contact" ]
]
]
]
, div [ class "inner cover" ]
[ h1 [ class "cover-heading" ]
[ text "Distance converter" ]
, div [ class "col-lg-12 mb-5" ]
[ div [ class "input-group mb-3" ]
[ div [ class "input-group-prepend" ]
[ button [ class "btn btn-seconda
[ text "Kilometers:" ]
]
, input [ attribute "aria-describedby
[]
, div [ class "input-group-append" ]
[ button [ class "btn btn-seconda
[ text "Miles:" ]
, button [ class "btn btn-seconda
[ text "0.0" ]
]
]
]
]
, div [ class "inner cover" ]
[ h1 [ class "cover-heading" ]
[ text "Weight converter" ]
, div [ class "col-lg-offset-12" ]
[ div [ class "input-group mb-3" ]
[ div [ class "input-group-prepend" ]
[ button [ class "btn btn-seconda
[ text "Kilograms:" ]
]
, input [ attribute "aria-describedby
[]
, div [ class "input-group-append" ]
[ button [ class "btn btn-seconda
[ text "Pounds:" ]
, button [ class "btn btn-seconda
[ text "0.0" ]
]
]
]
]
, footer [ class "mastfoot" ]
[ div [ class "inner text-secondary" ]
[ p []
[ text "Cover template for "
, a [ href "https://getbootstrap.com/
[ text "Bootstrap" ]
, text ", by "
, a [ href "https://twitter.com/mdo"
[ text "@mdo" ]
, text "."
]
]
]
]
]
]

Now, we have given our app a static view that gets


rendered to our web page. However, we are serving
no custom styles from the Cover template we got
from the official Bootstrap site.
There is a great CDN that can help us with that,
called RawGit, available at https://rawgit.com/. What
RawGit does is let you use any raw file from
GitHub, paste in its address, and get back a CDN
link that you can use in your apps.
So, for our custom Cover template’s CSS to be
included in our app, we can just locate its raw
address on GitHub, which is as follows: https://raw.gi
thubusercontent.com/twbs/bootstrap/v4-dev/docs/4.0/examples/cov
er/cover.css. Now that we have the raw link, we can
just paste it into RawGit, and use the following link
that it gives us: https://rawgit.com/twbs/bootstrap/v4-dev/do
cs/4.0/examples/cover/cover.css.

Now, we can add the Cover template styles too, just


under the bootstrapCss function:

coverTemplateCss =
let
tag =
"link"

attrs =
[ attribute "rel" "stylesheet"
, attribute "property" "stylesheet"
, attribute "href" "//rawgit.com/twbs/bootstrap/v
]

children =
[]
in
node tag attrs children

We also need to call these styles in our view


function. The update will occur at the beginning of
the view function so that its first few lines look like
this:

view model =
div [ class "site-wrapper" ]
[ bootstrapCss
, coverTemplateCss
, div [ class "site-wrapper-inner" ]
...

Now that we have saved everything and run our


app, we’ll see the static view with all the styles
served. There is just a slight improvement that
needs to be made, and that is that the div with the
class of site-wrapper-inner needs to be set to the width of
100vh. Let’s do that now in our view function.
Locate the following line of code in the view
function:

, div [ class "site-wrapper-inner" ]

Now, replace that line of code with the improved


code that adds the inline style using the style
function:

, div [ style [( "height", "100vh" )], class "site-wrapper-in

Finally, let’s change the buttons on our app by


giving them a different class. In Main.elm’s view
function, find all the instances of btn-secondary and
replace them with btn-warning.
You also might want to change that dark
background color to something more pleasing.
Unfortunately, we cannot just replace one class
with another, since the background color is
specified in the custom cover.css file that we are
serving from the CDN.
To make things more interesting, we’ll use an
online CSS gradient generator, available at https://ui
gradients.com/#VeryBlue. This link points to their
#VeryBlue gradient, but feel free to use another
that suits you.
The CSS we generated from the gradient website is
as follows:

background: #0575e6; /* fallback for old browsers */


background: -webkit-linear-gradient(to right, #0575e6, #021b7
background: linear-gradient(to right, #0575e6, #021b79); /* W

Now, we can convert it to inline Elm styles and


update the outermost div of our view function so that
the first dozen lines of our view function look like
this:
view model =
div
[ style
[ ( "background", "#0575e6" )
, ( "background", "-webkit-linear-gradient(to rig
, ( "background", "linear-gradient(to right, #057
]
, class "site-wrapper"
]
[ bootstrapCss
, coverTemplateCss
, div [ style [ ( "height", "100vh" ) ], class "site-

Let’s update the input fields now:


, div []
[ div [ class "input-group-prepend" ] []
, button [ class "btn btn-warning", type_ "button" ] [ t
, input
[ attribute "aria-describedby" "basic-addon1"
, attribute "aria-label" ""
, class "form-control"
, placeholder "Enter a numerical value"
, type_ "text"
, onInput Convert
]
[]
, button [ onClick Swap ] [ text model.unit2 ]
, button [ id "unit2Value" ] [ text (toString model.conv
]

Now, let’s introduce the append div:


, div []
[ div [ class "input-group-prepend" ]
[ button [ class "btn btn-warning", type_ "button" ]
, input
[ attribute "aria-describedby" "basic-addon1"
, attribute "aria-label" ""
, class "form-control"
, placeholder "Enter a numerical value"
, type_ "text"
, onInput Convert
]
[]
, div [ class "input-group-append" ] []
, button [ onClick Swap ] [ text model.unit2 ]
, button [ id "unit2Value" ] [ text (toString model.c
]
]
]

The preceding code produces a nice-looking site.


However, we still need to add the preceding view
information to our functioning app. We’ll do that
next.
Adding the improved
view to our functioning
app
At the beginning of this chapter, we built our app’s
functionality. After that, we built a design from
scratch, based on a free Bootstrap 4 template. Now,
it’s time to get the best of both worlds.
To begin, switch back to the app we called unit-
converter-complex. Next, simply copy and paste
the slightly-improved code into the updated
UnitConverter.elm file:

module UnitConverter exposing (..)

import Html exposing (beginnerProgram, button, div, input, la


import Html.Attributes exposing (attribute, class, for, id, s
import Html.Events exposing (onClick, onInput)

bootstrapCss : Html.Html msg


bootstrapCss =
let
tag =
"link"

attrs =
[ attribute "rel" "stylesheet"
, attribute "property" "stylesheet"
, attribute "href" "//maxcdn.bootstrapcdn.com/boo
]

children =
[]
in
node tag attrs children

coverTemplateCss : Html.Html msg


coverTemplateCss =
let
tag =
"link"

attrs =
[ attribute "rel" "stylesheet"
, attribute "property" "stylesheet"
, attribute "href" "//gitcdn.xyz/repo/twbs/bootst
]

children =
[]
in
node tag attrs children

type alias Model =


{ unit1 : String
, unit2 : String
, ratio : Float
, convertedValue : Float
}

init : String -> String -> Float -> Model


init unit1 unit2 ratio =
Model unit1 unit2 ratio ratio

view : { b | convertedValue : a, unit1 : String, unit2 : Stri


view model =
div
[ style
[ ( "background", "#0575e6" )
, ( "background", "-webkit-linear-gradient(to rig
, ( "background", "linear-gradient(to right, #057
, ( "height", "100vh" )
]
, class "site-wrapper"
]
[ bootstrapCss
, coverTemplateCss
, div []
[ label [ for "unit1Input" ] [ text model.unit1 ]
, input [ id "unit1Input", onInput Convert ] []
, button [ onClick Swap ] [ text "Switch" ]
, label [ for "unit2" ] [ text model.unit2 ]
, div [ id "unit2Value" ] [ text (toString model.
]
]
type Msg
= Swap
| Convert String

update :
Msg
-> { b | convertedValue : Float, unit1 : a, unit2 : a, ra
-> { b | convertedValue : Float, unit1 : a, unit2 : a, ra
update msg model =
case msg of
Swap ->
{ model | unit1 = model.unit2, unit2 = model.unit

Convert newValue ->


let
floatValue =
Result.withDefault 1 (String.toFloat newV
in
{ model | convertedValue = floatValue * model.rat

Next, we’ll focus on changing the following section:


, div []
[ label [ for "unit1Input" ] [ text model.unit1 ]
, input [ id "unit1Input", onInput Convert ] []
, button [ onClick Swap ] [ text "Switch" ]
, label [ for "unit2" ] [ text model.unit2 ]
, div [ id "unit2Value" ] [ text (toString model.
]

The first improvement we will make will be a


simple replacement of other function calls with
button functions where needed:
, div []
[ button [ for "unit1Input" ] [ text model.unit1 ]
, input [ id "unit1Input", onInput Convert ] []
, button [ onClick Swap ] [ text model.unit2 ]
, button [ id "unit2Value" ] [ text (toString model.convert
]

Next, we’ll slowly start building our more


Bootstrap-like look:
, div []
[ div [ class "input-group-prepend" ] []
, button [ class "btn btn-warning" ] [ text model.unit1 ]
, input [ id "unit1Input", onInput Convert ] []
, button [ onClick Swap ] [ text model.unit2 ]
, button [ id "unit2Value" ] [ text (toString model.conver
]

Next, we’ll pull in everything inside the wrapping


div:

, div []
[ div [ class "input-group-prepend" ]
[ button [ class "btn btn-warning", type_ "button" ]
, input
[ attribute "aria-describedby" "basic-addon1"
, attribute "aria-label" ""
, class "form-control"
, placeholder "Enter a numerical value"
, type_ "text"
, onInput Convert
]
[]
, button [ onClick Swap ] [ text model.unit2 ]
, button [ id "unit2Value" ] [ text (toString model.c
]
]
]

Now, let’s pull in the append buttons:


, div [ class "input-group-append" ]
[ button [ onClick Swap ] [ text model.unit2 ]
, button [ id "unit2Value" ] [ text (toString model.conve
]
]

After building up the site slowly from the inside


out, the final code will be as follows. The Main.elm file
will look like this:
module Main exposing (..)

import Html exposing (..)


import Html.Attributes exposing (..)
import UnitConverter exposing (..)

bootstrapCss : Html.Html msg


bootstrapCss =
let
tag =
"link"

attrs =
[ attribute "rel" "stylesheet"
, attribute "property" "stylesheet"
, attribute "href" "//maxcdn.bootstrapcdn.com/boo
]

children =
[]
in
node tag attrs children

coverTemplateCss : Html.Html msg


coverTemplateCss =
let
tag =
"link"

attrs =
[ attribute "rel" "stylesheet"
, attribute "property" "stylesheet"
, attribute "href" "//gitcdn.xyz/repo/twbs/bootst
]

children =
[]
in
node tag attrs children

-- Model

type alias Model =


{ lengthConverter : UnitConverter.Model
, weightConverter : UnitConverter.Model
}

initModel : Model
initModel =
{ lengthConverter = UnitConverter.init "Miles" "Kilometer
, weightConverter = UnitConverter.init "Kilograms" "Pound
}

-- Update

type Msg
= LengthConverterMsg UnitConverter.Msg
| WeightConverterMsg UnitConverter.Msg

update :
Msg
->
{ c
| lengthConverter :
{ b1
| convertedValue : Float
, ratio : Float
, unit1 : a1
, unit2 : a1
}
, weightConverter :
{ b
| convertedValue : Float
, ratio : Float
, unit1 : a
, unit2 : a
}
}
->
{ c
| lengthConverter :
{ b1
| convertedValue : Float
, ratio : Float
, unit1 : a1
, unit2 : a1
}
, weightConverter :
{ b
| convertedValue : Float
, ratio : Float
, unit1 : a
, unit2 : a
}
}
update msg model =
case msg of
LengthConverterMsg msg_ ->
let
newLengthConverter =
UnitConverter.update msg_ model.lengthCon
in
{ model | lengthConverter = newLengthConverter }

WeightConverterMsg msg_ ->


let
newWeightConverter =
UnitConverter.update msg_ model.weightCon
in
{ model | weightConverter = newWeightConverter }

-- View

view :
{ c
| lengthConverter :
{ b | convertedValue : a, unit1 : String, unit2 :
, weightConverter :
{ b1 | convertedValue : a1, unit1 : String, unit2
}
-> Html Msg
view model =
div
[ style
[ ( "background", "#0575e6" )
, ( "background", "-webkit-linear-gradient(to rig
, ( "background", "linear-gradient(to right, #057
]
]
[ bootstrapCss
, coverTemplateCss
, div [ class "site-wrapper-inner" ]
[ div [ class "cover-container" ]
[ header [ class "masthead clearfix" ]
[ div [ class "inner" ]
[ h3 [ class "masthead-brand" ]
[ text "Unit Converter Site" ]
, nav [ class "nav nav-masthead" ]
[ a [ class "nav-link text-second
[ text "Home" ]
, a [ class "nav-link text-second
[ text "Features" ]
, a [ class "nav-link text-second
[ text "Contact" ]
]
]
]
, h1 [ class "cover-heading" ]
[ text "Distance and Weight converter" ]
, UnitConverter.view model.lengthConverter |>
, UnitConverter.view model.weightConverter |>
]
]
]

-- Main

main : Program Never Model Msg


main =
beginnerProgram
{ model = initModel
, view = view
, update = update
}

The UnitConverter.elm file will look like this:

module UnitConverter exposing (..)

import Html exposing (..)


import Html.Attributes exposing (..)
import Html.Events exposing (onClick, onInput)

type alias Model =


{ unit1 : String
, unit2 : String
, ratio : Float
, convertedValue : Float
}

init : String -> String -> Float -> Model


init unit1 unit2 ratio =
Model unit1 unit2 ratio ratio

view : { b | convertedValue : a, unit1 : String, unit2 : Stri


view model =
div [ class "site-wrapper" ]
[ div [ class "inner cover" ]
[ div [ class "col-lg-12 mb-5" ]
[ div [ class "input-group mb-3" ]
[ div [ class "input-group-prepend" ]
[ button [ class "btn btn-warning", t
]
, input
[ attribute "aria-describedby" "basic
, attribute "aria-label" ""
, class "form-control"
, placeholder "Enter a numerical valu
, type_ "text"
, onInput Convert
]
[]
, div [ class "input-group-append" ]
[ button [ onClick Swap, class "btn b
, button [ id "unit2Value", class "bt
]
]
]
]
]

type Msg
= Swap
| Convert String

update :
Msg
-> { b | convertedValue : Float, unit1 : a, unit2 : a, ra
-> { b | convertedValue : Float, unit1 : a, unit2 : a, ra
update msg model =
case msg of
Swap ->
{ model | unit1 = model.unit2, unit2 = model.unit

Convert newValue ->


let
floatValue =
Result.withDefault 1 (String.toFloat newV
in
{ model | convertedValue = floatValue * model.rat

There is a lot of code, but we have discussed all of it


already. Even though there are some changes from
what we had in the original design-focused file, the
important thing is that we have finally built a fully
functional, beautiful Unit Conversion website in
Elm.
Summary
In Chapter 5, Completing the Unit Conversion
Website in Elm, we improved our Unit Converter
app so that it works with multiple inputs, that is, it
can convert more than a single unit of
measurement. Along the way, we learned a number
of important concepts:

Using Html.map

Working with complex relationships


between different views

Working with multiple messages in views

Working with multiple models

Combining everything we learned in a


relatively complex app

In the first five chapters of this book, we have


followed a hands-on approach which has involved a
lot of coding and app building. For the most part,
we have briefly touched on theory only where it
was absolutely necessary. In the next chapter, we’ll
fill in the gaps in our knowledge by exploring the
Elm language in more detail.
Exploring Elm in
Greater Detail
Welcome to Chapter 6, Exploring Elm in Greater
Detail. While the previous chapter was more on the
practical side, this chapter will deal with more
theoretical concepts.
The topics we will cover include:

Destructuring values in Elm

How does Elm deal with randomness?

Commands in Elm

Subscriptions in Elm

Working with Html.program

After completing this chapter, you will be able to:

Improve your apps by understanding more


advanced Elm concepts

Send commands to deal with randomness

Subscribe to events

Desctructure values and work with them

Let’s begin the chapter by looking at a very


important topic: destructuring values in Elm.
Destructuring values in
Elm
Destructuring is an elegant and effective way to
extract values from data structures in
Elm. Destructuring is also referred to as pattern
matching. The syntax is terse, and for the
uninitiated, it can be slightly confusing, precisely
because it is so short. However, once understood, it
feels like a natural and practical way to get the
values out from Elm data structures.
Let’s look at an example of destructuring a tuple in
Elm. As a reminder, a tuple is a data structure
similar to a record. The difference is that it is
usually shorter, and we do not care about naming
the values stored in a tuple. If you really must
name values in your data structure, you need to use
records.
Destructuring tuples in
Elm using let-in
expressions
Let’s imagine that a 400-meter race took place. The
race involved five participants. We will use a tuple
to store all the times of all the race participants (in
seconds). Let’s use Elm REPL to store those times:

aTuple = (60,65,71,75,90)

The REPL will return the following:

(60,65,71,75,90) : ( number, number1, number2, number3, numbe

Now that we have the times, we want to do some


analysis. However, to perform the analysis, we first
need to know how to extract those values.
To extract the values, we’ll use a let-in expression.
The first task we have is to calculate the total time
of all the racers. We will do that by adding up all
the individual times, or, put differently, by adding
up all the values stored in the tuple. Easy enough:

let \
(a,b,c,d,e) = aTuple \
in \
a + b + c + d + e

The REPL returns the following:


271 : number
Let’s now calculate the average time by dividing the
result by 5 (since there were five racers):
let \
(a,b,c,d,e) = aTuple \
in \
(a + b + c + d + e) / 5

This time, the REPL returns the following:


72.2 : Float

How did we destructure the preceding tuple? To


understand what is happening in the preceding
code, we need to discuss this line of code:
(a,b,c,d,e) = aTuple \

The preceding line of code follows the let keyword,


which, as explained previously, allows us to scope
variables and use them only in the in part of a let-in
expression.
Thus, we have pattern-matched a five-tuple
(containing variables a, b, c, d, and e) to the specific
tuple we declared previously, and gave it the
name aTuple. In other words, if we pretend that Elm
REPL is a person, the preceding line of code is the
same as if we said: Hey Elm REPL, use the values
from aTuple, and match the values to letters
a,b,c,d, and e, because I want to perform some
calculations on those values.
Then in the in part of our let-in expression, we
simply performed our calculations as needed. The
preceding example shows what destructuring
essentially is.
In the next example, let’s see how easy it is to work
with only some of the values from our aTuple. We’ll
calculate the average time of the first three
runners. This is really easy. Along the lines of what
we did earlier, we’ll simply do this:

let \
(a,b,c,d,e) = aTuple \
in \
(a+b+c) / 3

This is what Elm REPL returns:


65.33333333333333 : Float

Finally, let’s calculate the time difference between


the last to the finish line and the first racer to have
crossed it. We’ll do another simple calculation:
let \
(a,b,c,d,e) = aTuple \
in \
e-a

The Elm REPL will return the following:


30 : number

Great, everything works as expected. Let’s now


imagine a similar use of tuple destructuring. This
time, we’ll be using a five-tuple of Strings. The goal
is to play around with destructuring only some
values from a tuple, just like we did before:

aTupleOfStrings = ("Hello", "Big", "Wild", "Funny", "World")

Next, let’s print out a ”Hello World” to the REPL:

let \
(a, b) = aTupleOfStrings
in \
a ++ e

Unfortunately, this will not work like we thought it


would. The error is self-explanatory:

-- TYPE MISMATCH --------------------------------------------

`aTupleOfStrings` is being used in an unexpected way.

5| (a,e) = aTupleOfStrings
^^^^^^^^^^^^^^^
Based on its definition, `aTupleOfStrings` has this type:

( String, String, String, String, String )

But you are trying to use it as:

( a, b )

This poses a problem. While we already know, from


the examples of calculating race times, that we
could pattern-match all the values from a tuple
using a one-to-one mapping, with temporary
variables, this approach might not always be the
most efficient. Why?
Because sometimes we might have to assign longer,
more expressive variable names in our let
expressions, to satisfy the compiler. And then, once
we don’t get the error, we’d never actually use those
variable names. This is impractical. Realistically,
we will not be able to always type one-letter
temporary variables and still be able to easily make
sense of our programs.
Luckily, the solution is simple—when we don’t care
about some of the values that will appear in a tuple
while we are destructuring it, we can use the
underscore character to signify it. Effectively, what
we are saying to the Elm REPL is—don’t pay
attention to these values. Let’s rewrite the previous
example so that it works with underscores:

let \
(a,_,_,_,e) = aTupleOfStrings \
in \
a ++ e

The Elm REPL responds with the following:


''HelloWorld'' : String

Let’s replace the one-letter variables with


something more meaningful:

let \
(greeting,_,_,_,planetDescription) = aTupleOfStrings \
in \
greeting ++ planetDescription

The Elm REPL will still return the same result.


Destructuring tuples in
Elm using case-of
expressions
In this section, we will destructure tuples using
case-of expressions. To see the effects of tuple

destructuring, we’ll have to use the Ellie app rather


than Elm REPL. To begin, let’s set up the following
basic example in the Ellie app:
module Main exposing (main)

import Html exposing (Html, text)

greeting = ("Hello", "World")

main : Html msg


main =
case greeting of
("Hello", "World") ->
text "Tuple contained: Hello, World!"

("Hello", _) ->
text "Tuple contained: Hello "

(_, "World") ->


text "Tuple contained: World!"

(_, _) ->
text "There were neither 'Hello' nor 'World' in t

What the preceding code does should be easy to


understand—using the values from the greeting
variable, the main function will pattern-match
using the case-of expression, and return text based
on the values destructured from the tuple.
First we check for literal match on both Strings in
the two-tuple of greeting. Then, we check for literal
match on only the first String. Next, we check only
for the match of the "World"—the second String in our
greeting two-tuple. Finally, we cover all other
possible cases by matching for anything else, using
the following pattern:

(_, _) ->

Let’s now test our application with various


combinations. First, we’ll use the code as is.
Pressing the Compile button in the Ellie app will
produce the following text printed to the screen:
Tuple had: Hello, World!

Let’s change the greeting tuple so that it looks as


follows:
greeting = ("Howdy", "World")

Now, compile the Ellie app, and you should get the
following:

Tuple had: World!

Finally, let’s try changing the greeting to this:

greeting = ("Howdy", "Earth")

Upon compilation, the Ellie app will produce the


following output:
There were neither 'Hello' nor 'World' in the greeting Tuple

In the next section, we’ll use our newly-found


knowledge of tuple destructuring and apply it in an
improved version of our FizzBuzz app.
Building a FizzBuzz
app using tuple
destructuring inside of
a case-of expression
Let’s revisit our FizzBuzz problem. This time we
can use our modulus operations as conditions of a
case expression. The code will be as follows:

module Main exposing (main)

import Html exposing (..)

n = 100
modulusTest = (n % 3, n % 5)

main : Html msg


main =
text <| let fizzBuzz n =
case modulusTest of
(0, 0) -> "fizzBuzz"
(0, _) -> "fizz"
(_, 0) -> "buzz"
(_,_) -> toString n
in fizzBuzz n

To keep the preceding code concise, we have set the


value of n to a single fixed number of 100. Feel free
to test out the app by replacing that value with a
different number.
The preceding code should be easy to understand—
we set our modulusTest to a tuple that contains the
calculation of n % 5 and n % 3. Then, in main, we use
the text function on the let-in expression. The <| is
the backward function application operator. It
takes the value that gets evaluated to its right, and
passes it as an argument to whatever sits on its left
—in this case, a text function. Essentially, it is used
in place of parentheses. In other words, we could
have written the main function as follows:
main =
text (let fizzBuzz n =
case modulusTest of
(0, 0) -> "fizzBuzz"
(0, _) -> "buzz"
(_, 0) -> "fizz"
(_,_) -> toString n
in fizzBuzz n)

It would do the exact same thing as the code we


used prior to the preceding snippet. Finally, our
case expression takes the value returned from the
calculations performed in modulusTest and pattern-
matches it to appropriate values. The beauty of this
setup is that now it is very easy to check for
different numbers. For example, we could print out
threes and sevens, as follows:
module Main exposing (main)

import Html exposing (..)

n = 70
modulusTest = (n % 3, n % 7)

main : Html msg


main =
text (let fizzBuzz n =
case modulusTest of
(0, 0) -> "Divisible by 21, seven, and three"
(0, _) -> "Divisible by three"
(_, 0) -> "Divisible by seven"
(_,_) -> toString n
in fizzBuzz n)

Let’s now look at how to destructure tuples inside


of tuples.
Destructuring nested
tuples using let-in
expressions
Let’s try this in Elm REPL:

nestedTuples = (1, 2, 3, (5, 10, 15, (100, 50, 0)))

The REPL will return the following:

(1,2,3,(5,10,15,(100,50,0)))
: ( number
, number1
, number2
, ( number3, number4, number5, ( number6, number7, numb
)

Next, let’s destructure the value of i from the


innermost tuple:
let \
(a, b, c, (d, e, f, (g, h, i))) = nestedTuples \
in \
i

The REPL returns the following:

0 : number

Note that we did not even have to declare the


nestedTuples variable. Instead, we could have
destructured values on the fly, inside the let-in
statement. For example, let’s use the Ellie app to
compile the following code:
module Main exposing (main)

import Html exposing (..)

main : Html msg


main =
text (toString (
let
(a,b,c,(d,e,f,(g,h,i))) = (1, 2, 3, (5, 10,
in
i
))

Finally, we could have used the underscore when


suitable, as follows:
module Main exposing (main)

import Html exposing (..)

main : Html msg


main =
text (toString (
let
(_,_,c,(_,_,f,(_,_,i))) = (1, 2, 3, (5, 10,
in
i
))

With this, we wrap up our discussion of tuple


destructuring. Next, we’ll look at record
destructuring.
Destructuring records
in Elm using let-in
expressions
Records in Elm are destructured similar to tuples.
Let’s rewrite our example of race times using a
record instead of a tuple. Compile the following
app in the Ellie app:

module Main exposing (main)

import Html exposing (..)

raceTimes = { first = 60, second = 65, third = 71, fourth = 7

averageRunnerTime record =
let
{first, second, third, fourth, fifth} = record
in
(first + second + third + fourth + fifth) / 5

main : Html msg


main =
text (toString (
averageRunnerTime raceTimes
))

Once compiled, the following text should be


displayed on the screen:

72.2

The code used in the preceding app is essentially


the same code we used in the example on
destructuring tuples. The difference comes from
the way that records work in Elm—since they must
have named values, we must use these names when
destructuring values out of records.
Destructuring records
on the fly
When destructuring records, we don’t even have to
use the underscore character. Since all the values in
a record are named, it’s enough to use that name to
extract the value from a record. Using our previous
example of race times, let’s put the record that
stores them inside Elm REPL:
raceTimes = { first = 60, second = 65, third = 71, fourth = 7

The REPL returns the following:

{ first = 60, second = 65, third = 71, fourth = 75, fifth = 9


: { fifth : number
, first : number1
, fourth : number2
, second : number3
, third : number4
}

Next, let’s look at extracting the time of the fifth


runner:
{ fifth } = raceTimes

The REPL returns the following:

90 : number

Let’s use the Ellie app to destructure the time of the


fifth runner:
module Main exposing (main)

import Html exposing (..)

extractorFunction { fifth } =
fifth

raceTimes = { first = 60, second = 65, third = 71, fourth = 7


fifth = raceTimes

main : Html msg


main =
text (toString (
extractorFunction raceTimes
))

But why do we need to use our custom


extactorFunction? Can’t we just go without it? Let’s try

this:
module Main exposing (main)

import Html exposing (..)

raceTimes = { first = 60, second = 65, third = 71, fourth = 7


fifth = raceTimes

main : Html msg


main =
text (toString (
fifth
))

Upon compilation, the Ellie app will print this on


the screen:

{ first = 60, second = 65, third = 71, fourth = 75, fifth = 9

If we really wanted to do it without the extractor


function, we could have done it like this:
module Main exposing (main)
import Html exposing (..)

raceTimes = { first = 60, second = 65, third = 71, fourth = 7


fifth = raceTimes

main : Html msg


main =
text (toString (
raceTimes.fifth
))

When compiled, the Ellie app will print out the


expected value of 90 on the screen. Finally, let’s
deconstruct and use only two values. We will begin
with faulty code, which will throw an error. The
code will look as follows:
module Main exposing (main)

import Html exposing (..)

extractorFunction { first, fifth } =


first, fifth

raceTimes = { first = 60, second = 65, third = 71, fourth = 7


{first, fifth} = raceTimes

main : Html msg


main =
text (toString (
extractorFunction raceTimes
))

The preceding code, when compiled, will throw the


following error:
SYNTAX PROBLEM
Line 6, Column 10
I am looking for one of the following things:

"'"
a field access like .name
an expression
an infix operator like (+)
end of input
more letters in this name
whitespace

Why? Give yourself some time to think it through.


It will be a useful discovery. For the impatient
reader, here is the appropriate code:
module Main exposing (main)

import Html exposing (..)

extractorFunction { first, fifth } =


(first, fifth)

raceTimes = { first = 60, second = 65, third = 71, fourth = 7


{first, fifth} = raceTimes

main : Html msg


main =
text (toString (
extractorFunction raceTimes
))

The error was due to the fact that initially, we gave


a confusing expression to the Elm compiler, and it
simply did not know what to do with it. Let’s look
at the offending code again:
extractorFunction { first, fifth } =
first, fifth

The compiler sees: a value, a comma, and a value,


and it has no idea how to return a single value from
this line of code. We’ve already seen one way of
solving this error; by extracting the values from a
record into a tuple.
An alternative approach would be to print these
values out as a string, like so:
module Main exposing (main)

import Html exposing (..)


raceTimes = { first = 60, second = 65, third = 71, fourth = 7

main : Html msg


main =
text <|
toString (raceTimes.first)
++ ", "
++ toString (raceTimes.fifth)

With this, we have completed our discussion of


destructuring values in Elm. Next, we will look into
the ways in which Elm deals with randomness.
Dealing with
randomness in Elm
As mentioned before, values in Elm are immutable.
The functions are pure; they allow for no side
effects. When a specific value goes in, a function
will operate on it and always return another
specific value.
This poses a problem—just how do we deal with
randomness? For example, how do we generate
random numbers? Or, for that matter, how do we
perform anything that involves a side effect? We do
that by having functions return commands.
Commands in Elm
With a command, you are telling the Elm runtime
to do something you are not allowed to do, since
doing it would break the concept of guarantees.
Thus, a command that you return from a function
is just a static, immutable value. What does this
value do? It just names the desired result. It does
not tell Elm how to do it. It is just a name for one or
more things that need to be done by the Elm
runtime.
For example, since the concept of guarantees says
that for every input into a pure function, we should
receive the same kind of output, we cannot have a
function return a random number, since doing so
would break the concept of guarantees. In order to
make the preceding scenario possible, we need to
send a command to the Elm runtime, asking it to
give us a random number.
Thus, once the Elm runtime receives a command,
such as a request for a random number, it will
return a message. Then we can use that message in
the update function, as explained in Chapter 2,
Building Your First Elm App.
The goal of this very basic introduction to
commands in Elm is that there are no ambiguities
about the new code that we will introduce, or, put
differently, that we have at least some knowledge
about what each piece of code does. Next, we’ll look
at subscriptions, and how they fit into the Elm
architecture.
Subscriptions in Elm
Commands allow us to tell the Elm runtime to do
random things without breaking Elm’s guarantees.
However, let’s say we want the Elm runtime to tell
us when some changes happen in the outside world
(that is, anything that we cannot directly control in
our app). We cannot control the changes in the
outside world with commands—because we are not
the source of these changes.
That’s why subscriptions exist. They allow us to
listen for things such as mouse movements,
keyboard presses, time changes, and so on. With
commands, we order the Elm runtime to do
random things; with subscriptions, the Elm
runtime tells us of random things being done.
For example, we want to track when a keyboard
button is pressed. We’ll have our app subscribe to
those keyboard presses. Once that specific
keyboard button is pressed, the Elm runtime will
send a message, and in our update function, we
specify how it should behave when such a message
is received.
Now that we are familiar with commands and
subscriptions in Elm, we can look at how they can
be used to extend our current concept of the Elm
architecture.
Improving the Elm
architecture by adding
effects
Before we knew about commands in Elm, we used
to implement the Elm architecture using the
beginnerProgram function:

main =
Html.beginnerProgram
{ model = model
, view = view
, update = update
}

However, using only beginnerProgram, we have no way


to deal with either commands or subscriptions.
Practically, our beginnerProgram function is just a
watered-down version of the Html.program function.
Put differently, using Html.program, we have the ability
to extend our Elm architecture so that we keep
Elm’s guarantees and still deal with randomness,
that is, side effects, regardless of these side effects’
origin.
Thus, rather than using Html.beginnerProgram (like we
did previously), let’s see how we can set up our
main function so that it is using Html.program:
main =
Html.program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
The change in the code is minimal, but our Elm
program just got a lot more capable. This concludes
our brief introduction to dealing with randomness
in Elm. In the next section, we will look at a very
useful concept in Elm: partial application.
Understanding partial
application
Let’s say we have a function that takes in two
parameters. The function is very simple—it just
takes two Strings and concatenates them, with an
added space in between.
The function signature will look as follows:

concatTwoWords wordOne wordTwo = wordOne ++ " " ++ wordTwo

When we call the function, we might pass two


words like this:

concatTwoWords "User" "Experience"

The preceding function will evaluate to the


following:
"User Experience"

Let’s now implement this as a tiny program that we


can run in the Ellie app:
module Main exposing (main)

import Html exposing (Html, text)

concatTwoWords : String -> String -> String


concatTwoWords wordOne wordTwo = wordOne ++ " " ++ wordTwo

main : Html msg


main =
text (concatTwoWords "User" "Experience")
Upon compilation, the Ellie app will show the
words User Experience in the right-hand preview
pane:

Note that we have also added type annotation


preceding the function definition. The type
annotation reads:
concatTwoWords : String -> String -> String

Looking at the type annotation, we could say that


our concatTwoWords function takes two strings and
returns a string. After all, that is what the type
annotation looks like. What would happen if we
passed only one argument to our concatTWoWords
function? Let’s try it. Run the following code in the
Ellie app:
module Main exposing (main)

import Html exposing (Html, text)

concatTwoWords : String -> String -> String


concatTwoWords wordOne wordTwo = wordOne ++ " " ++ wordTwo

main : Html msg


main =
text (concatTwoWords "User")

This time we got a Type Mismatch error, which reads as


follows:
The argument to function text is causing a mismatch.

Function text is expecting the argument to be:


String
But it is:

String -> String


Hint: It looks like a function needs 1 more argument.

So, obviously, if our function is expecting two


arguments, we can’t just pass one argument to it
and call it a day, since, as we have just seen, that
will cause the compiler to throw an error.
Instead, what we can do is a neat little trick. We
can define a new function. This new function that
we’ll define will store the result of passing only one
String to concatTwoWords. In other words, it will store
the result of partial application of the concatTwoWords
function.
For lack of a better name, we will give this new
function the name partiallyApply, and define it as
follows:
partiallyApply = concatTwoWords wordOne

What we have done here is that, we have partially


applied our concatTwoWords function by passing it only
one argument. Then, we stored the result in
another function that we called partiallyApply. We
can still pass an argument to our partiallyApply
function, and we will store it in the partiallyApplyAgain
function, as follows:
partiallyApplyAgain = partiallyApply wordTwo

The updated code, ready to be run in the Ellie app,


will now look as follows:

module Main exposing (main)

import Html exposing (Html, text)


wordOne = "User"
wordTwo = "Experience"

concatTwoWords : String -> String -> String


concatTwoWords wordOne wordTwo = wordOne ++ " " ++ wordTwo

partiallyApply = concatTwoWords wordOne


partiallyApplyAgain = partiallyApply wordTwo

main : Html msg


main =
text (partiallyApplyAgain)

Running the preceding code will result in the


words User Experience displayed on the right-hand
pane of the Ellie app.
Summary
In this chapter, we have covered a number of
important topics, namely:

Destructuring values in Elm

How does Elm deal with randomness?

Understanding partial application

Introducing effects to our apps by using


Html.program

In the next chapter, we will begin building a fully


functional weather app which will connect to a
remote API to fetch weather information.
Making a Weather App
in Elm
Welcome to Chapter 7, Making a Weather App in
Elm. In this chapter, we will make our Elm-
powered weather app. The purpose of this app is to
learn how to fetch information from JSON and use
it in our app.
The topics we will cover include:

Taking care of errors with Result

Dealing with optional values and


nothingness using Maybe

Decoding JSON strings using decoders

Fetching remote data with the help of the


HTTP package

Working with a third-party API

After completing this chapter, you will be able to:

Fetch information from the internet in


JSON format

Have your Elm apps consume JSON data


using decoders

Understand how all the moving parts fit


together to build a functioning app
To begin, we will look at obtaining weather data
from a third-party API.
Obtaining the weather
data from a third-party
API
In order for our weather app to even be possible,
we will need to obtain weather data from
somewhere. Luckily, there are plenty of weather-
related APIs online, which makes this task a lot
easier to accomplish.
In order to have a working app, we need to connect
to a third-party data provider. In our case, the data
provider we will use is Open Weather Map.
The information about the Open Weather Map’s API is
available at https://openweathermap.org/api.

To gain access to the API you need to obtain the


APPID. The instructions on how to do that are
available here, http://openweathermap.org/appid. Basically,
all that is needed to obtain it is to create a new
account.
As soon as you have created your account, you will
find your API key generated under the API keys
tab. To access the data for a city, for example,
Chicago, it’s enough to visit the following URL:
http://api.openweathermap.org/data/2.5/weather?
q=chicago&APPID=abcdef1234567890

Obviously, the preceding URL is using the wrong


APPID. Replace it with your own, and you should
be good to go. Using the correct APPID in the
preceding URL, your browser will show a JSON
string like this:
{"coord":{"lon":-87.62,"lat":41.88},"weather":[{"id":802,"mai
Just what we do with this string will be discussed
later in this chapter.
What are we going to
build?
We are going to build a very simple weather app,
which will communicate with the outside world to
fetch JSON-formatted weather data.
Once we receive that weather data, we will then
decode the JSON string into values that our app’s
model will be able to understand and work with,
using decoders. Let’s jump right in.
Building our weather
app
We will start by replacing the code in Main.elm with
the bare-bones Elm code we have already been
using in the previous chapters:
module Main exposing (..)

import Html exposing (..)


import Html.Attributes exposing (..)
import Html.Events exposing (..)

-- Model
init =
{}

-- Update
type Msg
= Nothing

update msg model =


model

-- View
view model =
div [] [ text "Everything will go here" ]

-- Main
main =
beginnerProgram
{ model = init
, view = view
, update = update
}

Let’s immediately improve our app by replacing


beginnerProgram with Html.Program, since beginnerProgram, as

explained in the previous chapter, has no means of


working with side effects:
module Main exposing (..)
import Html exposing (..)

-- Model
type alias Model =
{}

init : ( Model, Cmd Msg )


init =
( Model, Cmd.none )

-- Update
type Msg
= Noop

update : Msg -> Model -> ( Model, Cmd Msg )


update msg model =
case msg of
Noop ->
( model, Cmd.none )

-- View
view : Model -> Html Msg
view model =
div [] [ text "Everything will go here, this time using H

-- Subscriptions
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none

-- Main
main : Program Never Model Msg
main =
Html.program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}

Let’s walk through the preceding code.


We start off by describing the application model as
a type alias called Model, and we set it to an empty
record. Following the type alias, we introduce the
init function, which holds the initial state of our
app, and the initial command to run. With Cmd.none,
we are basically saying to Elm runtime not to run
any commands at this point. So, although we are
not going to be requesting any commands to be run
at this moment in our code, we cannot just omit
them, we have to be explicit, and clearly state that
we will not run any commands just yet.
Note the change in the init function: the init function
now takes the following two-tuple: ( Model, Cmd.none ).

Next, in the update function, we are using a union


type Msg, which can be only of NoOp value. Basically,
whenever we want to assert that our app should not
do anything with a message it receives, we can use
NoOp as the value.

The update function uses a case of expression. We


pass two arguments to the update function: the msg
and the model, and these arguments are used to
update the model and return a command. Here, since
we are receiving the NoOp message, the only thing
that happens is: we just return the model as is. By
using Cmd.none in the returned two-tuple, we are
saying to the Elm runtime that it should not run
any commands.
In the subscriptions function, all that we are doing is:
we are saying to the Elm runtime that we do not
want to subscribe to any events. Hence, we assign it
the value of Sub.none. Finally, the main function gets
assigned the Html.program where all of the above
comes together.
In the next section, we’ll install the HTTP package.
Installing the HTTP
package
To install the HTTP package, we need to point our
console to the location of our weather app folder. Next,
run the following command in the console:
elm package install elm-lang/http 1.0.0

This is what the console will return:


To install elm-lang/http I would like to add the following
dependency to elm-package.json:

"elm-lang/http": "1.0.0 <= v < 2.0.0"

May I add that to elm-package.json for you? [Y/n]

After confirming with y, the console will output the


following:

Some new packages are needed. Here is the upgrade plan.

Install:
elm-lang/http 1.0.0

Do you approve of this plan? [Y/n]

Approving the plan by typing y will produce the


following messages in the console:
Starting downloads...

● elm-lang/http 1.0.0

Packages configured successfully!


Next, let’s open the elm-package.json and verify that it
looks as follows:
{
"version": "1.0.0",
"summary": "helpful summary of your project, less than 80
"repository": "https://github.com/user/project.git",
"license": "BSD3",
"source-directories": [
"src"
],
"exposed-modules": [],
"dependencies": {
"elm-lang/core": "5.1.1 <= v < 6.0.0",
"elm-lang/html": "2.0.0 <= v < 3.0.0",
"elm-lang/http": "1.0.0 <= v < 2.0.0"
},
"elm-version": "0.18.0 <= v < 0.19.0"
}

With this, we have successfully added the HTTP


package to our project.
The official documentation for the HTTP package is
available at http://package.elm-lang.org/packages/elm-lang/http/1.0.0/Htt
p.

Now that we have added all the necessary


packages, we can continue building our app.
Adding all the imports
Let’s begin by adding all the imports that we need
at the top of Main.elm:
module Main exposing (..)

import Html exposing (..)


import Html.Attributes exposing (..)

import Html.Events exposing (..)


import Http
import Json.Decode as Decode

The linter will throw four identical warnings after


you save your code with the preceding update. The
only difference, in each of the warnings, is the
module that is referenced. For example:

Module `Html.Attributes` is unused.


Best to remove it. Don't save code quality for later!

It is wonderful that the compiler encourages us to


improve our code quality at the first possible
opportunity, but for now, it is safe to ignore those
warnings. We will soon add the code to our app
that will resolve them.
Conceptualizing the
model
Since we will be changing up quite a lot of things in
our app, at this point we can prevent several
nagging errors by simply temporarily removing
type annotations. To do that, simply remove type
annotations from the following functions: init,
update, view, subscriptions, and main.

Let’s look at our updated model now:


type alias Model =
{ temperatureInfo : TemperatureInfo
, city : String
}

type alias TemperatureInfo =


{ name : String
, windSpeed : Float
, temperature : Float
, pressure : Float
, humidity : Float
}

The building blocks of our model should be self-


explanatory. Let’s then add the preceding code to
Main.elm, and save the file. Our app compiles just

fine, and everything works as it has. Let’s now


update the init function:
init =
( Model (TemperatureInfo "Did not load" 0 0 0 0) ""
, Cmd.none
)
What we did here is, we simply reflected the newly
introduced changes to our model, in our init
function. In the next section, we’ll set up the Msg and
the update functions.
Setting up Msg union
type
Our Msg union type is pretty simple:
type Msg
= GetTemp
| CityInput String
| NewTemp (Result Http.Error String)

There are three possible messages that the update


function can receive: GetTemp, CityInput, and NewTemp.
The most peculiar of them is the third one:
NewTemp (Result Http.Error String)

There is some code there that we haven’t seen


before. The NewTemp value holds a Result. That result
can be an Http.Error, which means that something
caused our HTTP request to fail. There can be a
number of reasons: the URL does not exist, or the
server did not respond, and so on. Another thing
that can happen is: the request can succeed.
In that case, we’ll get back the String as the result of
making a successful HTTP request.
Result and Maybe
This is a good time to make a little digression and
talk about Result and Maybe in some more detail. To
deal with errors, Elm also has Tasks, but we will not
discuss them at this time.
Working with Result
The official documentation for Result is available
at: http://package.elm-lang.org/packages/elm-lang/core/latest/R
esult. We use Result whenever there are things that

our code does that can potentially fail. Using Result


allows us to deal better with errors in Elm. The
Result definition says that it’s a union type that has

two type constructors: Ok and Err. Take a look at the


following code snippet:
type Result error value
= Ok value
| Err error

If an operation succeeds, the Result is Ok. Otherwise,


the Result is Err.
To test drive Result, all that we need to do is run an
operation that might fail. REPL is the perfect place
for such a test. Thus, let’s point our browser to htt
p://elmrepl.cuberoot.in/ and run the following:

import Date
Date.fromString "2018-02-18"

Running the preceding in the REPL, will return the


following:
Ok <Sun Feb 18 2018 00:00:00 GMT+0000 (UTC)> : Result.Result

Wonderful, we got back an Ok result from an


operation that could have failed. Let’s do it again,
for good measure:
Date.fromString "0"
You might think that the preceding would fail, but
it is actually completely valid:
Ok <Sat Jan 01 2000 00:00:00 GMT+0000 (UTC)> : Result.Result

Great, we still got back an Ok. This time, let’s use a


string of letters, to make sure that the REPL can’t
handle it:

Date.fromString "abc"

And indeed, the REPL doesn’t know how to deal


with this input. However, instead of throwing an
ugly exception, we get an elegant one:

Err "Unable to parse 'abc' as a date. Dates must be in the IS


: Result.Result String Date.Date

To drive the point home, in the preceding example,


we ran an expression that could potentially fail,
that is, we ran the Date.fromString function. The
Date.fromString function takes a parameter of type
String, and returns a Date. In the preceding code

snippet, we gave it the String with the value of abc,


and it returned an Err as the Result.
Resulthas its own module, and that is why the type
definition reads Result.Result.

Next, we’ll look at working with Maybe.


Working with Maybe
Also a union type, Maybe is a way to deal with
nothingness in Elm, or, put differently, with values
that are optional, that is, which might, or might not
exist. The official documentation, available at http://
package.elm-lang.org/packages/elm-lang/core/latest/Maybe,
gives the definition for the Maybe union type as
follows:
type Maybe a
= Just a
| Nothing

As we discussed earlier in the book, using the


letter a in Elm is a convention, and it’s a way to
describe that the value used at that position can be
anything. So the a in the preceding definition can
be a String, an Int, or any other value.
So, in Elm, Maybe can be only one of two things— Just
anything, or Nothing. More specifically, Maybe can be
either one of these:

Just a String, or Just an Int, or Just a Float…, or

Nothing

Let’s now turn to Elm REPL at http://elmrepl.cuberoot.i


n/ and see the Maybe type in practice:

testingMaybe = Just 1
The REPL will return the following:
Just 1 : Maybe.Maybe number

As we can see, Just 1 is a Maybe number . Let’s do another


one:

testingMaybe = Just 1.1

Running the preceding line will result in this:


Just 1.1 : Maybe.Maybe Float

Finally, let’s see a Nothing in action:


testingMaybe = Nothing

REPL replies with the following:


Nothing : Maybe.Maybe a

Since Nothing has no value, we are back to Maybe a. Elm


guarantees require us to have an a here, even
though Nothing is Nothing, and since it represents the
absence of value it does not need a type for its
value.
To destructure a Maybe, we can use the case-of
expression. Let’s see an example of that using Ellie:
module Main exposing (main)

import Html exposing (Html, text)

-- A person, but maybe we do not know their age.


type alias Person =
{ name : String
, age : Maybe String
}
tom = { name = "Tom", age = Just "42" }
sue = { name = "Sue", age = Nothing }

main =
text <|
case tom.age of
Nothing ->
"No age for this person"
Just val ->
val

In the preceding example, we are using a slightly


altered version of the person example available at
the official documentation for Maybe. The difference
in the preceding code is that instead of an Int, we
are using a String for the age entry in our record.
That way, we can avoid making our code any more
complex than it needs to be, and we still return
a String from all the branches in our case-of
expression.
The app will print out 42 upon compilation. Now,
let’s change the app so that we are attempting to
print Sue’s age. The only difference to be made is
listed as follows:

case sue.age of

This time, upon compilation, the app will pattern-


match the Nothing branch of the case-of expression in
main, and that will result in the "No age for this person"
message printed on the screen.
Result and Maybe with
Defaults
Resultand Maybe are quite similar. If our operation
succeeds, we get an Ok a with Result. When dealing
with Maybe, we get Just a if a value is present.
In case there was an error, for Result we get Err error.
In case of no value in Maybe, we get Nothing. We have
already looked at using Result.withDefault earlier in
the book (in Chapter 4, Preparing a Unit Conversion
Website in Elm). Let’s do a quick recap by running
the following in Elm REPL:

Result.withDefault 0 (Ok 1)

The REPL responds with the following:

1 : number

Now let’s try using Strings:

Result.withDefault "This is default." (Ok "This is OK")

The REPL returns the following:


"This is OK" : String

Similar to Result, we can also use withDefault on Maybe.


Run the following in the REPL:
Maybe.withDefault 0 Nothing

The REPL returns the following:


0 : number

Let’s do a couple more. First, let’s give it a default


String:

Maybe.withDefault "abc" Nothing

The REPL returns the following:


"abc" : String

How about a default Record?

Maybe.withDefault {} Nothing

Running the preceding code in the REPL will


produce the following:

{} : {}

In the next section, we will work on our update


function.
Updating the update
function
Our update function will be a lot more complex. First,
we need to cover all the preceding messages in our
update function. We’ll do that by adding a case-of

expression:

update msg model =


case msg of
GetTemp ->
( model, getTemperature model.city )

NewTemp (Ok json) ->


let
newTemperatureInfo =
decodeTemperatureInfo json
in
( { model | temperatureInfo = newTemperatureInfo

NewTemp (Err _) ->


( model, Cmd.none )

CityInput city ->


( { model | city = city }, Cmd.none )

In the preceding code snippet, we can see four


possible cases for our update function. In case our
update function receives the GetTemp message, it will
return the two-tuple that has the model and the
getTemperature value.

If the update function receives the NewTemp (Ok


json) message, that is, if we successfully receive a
JSON string from a remote server, then we use a
let-in expression to return the same model, updated
with the newTemperatureInfo.
The third pattern to match is the NewTemp (Err _). This
pattern will be matched in case we receive an error
from the remote server, in which case we will just
return the existing model - that is, (model, Cmd.none).
The last possible message that we can pattern-
match for in the update function, is the CityInput
message. If we get a CityInput message, we will take
the city String that gets passed to it, and return the
existing model, plus the new city String.
However, if we ran our app now, the compiler
would throw two errors:

Cannot find variable getTemperature

Cannot find variable decodeTemperatureInfo

Obviously, abstracting away complexity into


separate variables is great to make it easier to
reason about our app, but now we need to get into
the actual implementation of getTemperature and
decodeTemperatureInfo, in order to make our update
function work. But before we can do that, we will
need to look at decoders and encoders in greater
detail.
Decoders and encoders
To parse data from JSON, Elm uses decoders. To
do the opposite, Elm uses encoders. Using
decoders and encoders, we can translate data from
dynamically typed JSON, to statically typed Elm
data structures, and vice versa.
To begin, let’s look at the Json.Decode package,
available as part of the Elm language core. The
official documentation for Json.Decode is available at:
http://package.elm-lang.org/packages/elm-lang/core/5.1.1/Json-D

ecode . The official documentation defines a decoder


as:
A value that knows how to decode JSON values.
Decoding primitive
values
Let’s look at a few examples of decoding primitive
values, with the help of Elm REPL. To begin, let’s
decode a simple Int:
import Json.Decode exposing (..)
decodeString int "100"

The REPL returns the following:


Ok 100 : Result.Result String Int

So, we get back a Result, which is a sign of an action


that can potentially fail. Let’s see how it fails, by
providing a Bool to the preceding
expression, instead of an Int:

decodeString int "true"

Running the preceding code results in the REPL


returning the following:

Err "Expecting an Int but instead got: true" : Result.Result

Next, let’s decode a JSON float into an Elm Float:

decodeString float "1.234"

The REPL returns the following:


Ok 1.234 : Result.Result String Float
How about decoding a JSON string into an
Elm String?

decodeString string "abcd"

This time, the REPL responds with the following:


Err "Given an invalid JSON: Unexpected token a" : Result.Resu

Why is that error occuring? Because we need to


provide escaped quotes as well, so as to make it
work:

decodeString string "\"abcd\""

Now the REPL is not complaining:


Ok "abcd" : Result.Result String String

Finally, let’s look at decoding a JSON boolean into


an Elm Bool:
decodeString bool "false"

The REPL returns the following:

Ok False : Result.Result String Bool

Let’s examine each of the decoders’ signatures:


import Json.Decode exposing (..)
int
<decoder> : Json.Decode.Decoder Int
float
<decoder> : Json.Decode.Decoder Float
string
<decoder> : Json.Decode.Decoder String
bool
<decoder> : Json.Decode.Decoder Bool

As we can see in the preceding code snippet, int is a


decoder for Int, float is a decoder for Float, string is a
decoder for String, and bool is a decoder for Bool. Now
that we understand how primitives are decoded
from JSON into Elm, let’s look at decoding a more
complex JSON string, namely, the one that we got
back from the API at the beginning of this chapter.
Decoding a JSON
string returned from
the API
To begin, let’s look at the JSON string that we got
back from the API:
{
"coord":{
"lon":-87.62,
"lat":41.88
},
"weather":[
{
"id":802,
"main":"Clouds",
"description":"scattered clouds",
"icon":"03n"
}
],
"base":"stations",
"main":{
"temp":268.9,
"pressure":1025,
"humidity":86,
"temp_min":267.15,
"temp_max":270.15
},
"visibility":16093,
"wind":{
"speed":2.1,
"deg":260
},
"clouds":{
"all":40
},
"dt":1518941700,
"sys":{
"type":1,
"id":1030,
"message":0.0033,
"country":"US",
"sunrise":1518957687,
"sunset":1518996470
},
"id":4887398,
"name":"Chicago",
"cod":200
}

Obviously, we have formatted the JSON string so


as to make it easier to read. The first thing to note
is; we do not have to decode all of the preceding
code. We can choose to decode only as much data
as we need. So let’s assume that the only piece of
data that we are interested in is the second-to-last
key-value pair, namely: ''name'':''Chicago''.
How do we decode it out of the preceding chunk of
JSON? We’ll make a small Ellie app to see how this
can be done:
import Html exposing (..)
import Json.Decode exposing (..)

main = text (toString (decodeString weatherDataDecoder (json)

type alias WeatherData =


{ name : String }

json = """
{
"coord":{
"lon":-87.62,
"lat":41.88
},
"weather":[
{
"id":802,
"main":"Clouds",
"description":"scattered clouds",
"icon":"03n"
}
],
"base":"stations",
"main":{
"temp":268.9,
"pressure":1025,
"humidity":86,
"temp_min":267.15,
"temp_max":270.15
},
"visibility":16093,
"wind":{
"speed":2.1,
"deg":260
},
"clouds":{
"all":40
},
"dt":1518941700,
"sys":{
"type":1,
"id":1030,
"message":0.0033,
"country":"US",
"sunrise":1518957687,
"sunset":1518996470
},
"id":4887398,
"name":"Chicago",
"cod":200
}
"""

weatherDataDecoder =
Json.Decode.map
WeatherData
(field "name" string)

The preceding little app successfully decodes the


JSON string provided. As we can see, we are
decoding only the name key from our JSON object,
whose value, based on the JSON object provided
previously, is Chicago. The result of compiling the
app in the Ellie editor is displayed on the screen:
Ok { name = "Chicago" }

Let’s see how we can improve on the preceding


app. This time, we want to return not only the name
key from our JSON object, but also the id key:
import Html exposing (..)
import Json.Decode exposing (..)
main = text (toString (decodeString weatherDataDecoder (json)

type alias WeatherData =


{ id : Int
, name : String
}

json = """
{
"coord":{
"lon":-87.62,
"lat":41.88
},
"weather":[
{
"id":802,
"main":"Clouds",
"description":"scattered clouds",
"icon":"03n"
}
],
"base":"stations",
"main":{
"temp":268.9,
"pressure":1025,
"humidity":86,
"temp_min":267.15,
"temp_max":270.15
},
"visibility":16093,
"wind":{
"speed":2.1,
"deg":260
},
"clouds":{
"all":40
},
"dt":1518941700,
"sys":{
"type":1,
"id":1030,
"message":0.0033,
"country":"US",
"sunrise":1518957687,
"sunset":1518996470
},
"id":4887398,
"name":"Chicago",
"cod":200
}
"""

weatherDataDecoder =
Json.Decode.map2
WeatherData
(field "id" int)
(field "name" string)

If we compiled the app now, we’d get a slightly


different result from what we had last time:

Ok { id = 4887398, name = "Chicago" }

There are slight changes in the preceding app,


which make it possible to extract two values, rather
than just one, which we had in the previous version
of our little JSON-decoding app. Let’s look at the
changes:

type alias WeatherData =


{ id : Int
, name : String
}

In the WeatherData type alias, we added an id of type


Int to our Record:

weatherDataDecoder =
Json.Decode.map2
WeatherData
(field "id" int)
(field "name" string)

In the decoders, we are using the map2 function


instead of the map function, and we are adding
another JSON field, with the key of id, and the
expected value of the JSON integer. Analog to what
we have seen, in case we wanted to map over
another value from our JSON object, we would
need to use the map3 function.
Decoding nested
objects
Let’s say that we want to get the country
information from the returned JSON. The change
that we need to make to our decoder is simple:
instead of field, we will use at. While field uses a
string, at uses a list of strings, which allows us to go
into the structure of a nested object. Let’s update
our Ellie app’s weatherDataDecoder so that it looks as
follows:

weatherDataDecoder =
Json.Decode.map3
WeatherData
(field "id" int)
(field "name" string)
(at ["sys", "country"] string)

Let’s also update the WeatherData type alias:


type alias WeatherData =
{ id : Int
, name : String
, country: String
}

Note that in the preceding code snippet, we are


keeping our flat Record structure, and it does not
reflect the JSON object that we get the data from.
That is perfectly fine. Upon compilation, the app
will print the following result to the screen:

Ok { id = 4887398, name = "Chicago", country = "US" }


Now that we understand how decoders work, and
after a bit of practice with decoders, it should be a
lot easier to understand the code that we will see in
the next section.
Adding getTemperature
and
decodeTemperatureInfo
Our getTemperature is a simple let-in expression, which
will send a new request using Http.send and
Http.getString, so as to obtain the weather data from a
custom URL. The string of the custom URL to fetch
the data from, depends on the city variable, that is,
the value of the user input we get from the text
input field of our app:

getTemperature city =
let
url =
"http://api.openweathermap.org/data/2.5/weather?q
in
Http.send NewTemp (Http.getString url)

The decodeTemperatureInfo is also a let-in expression,


which, although possibly a bit scary-looking at first
glance, is actually just a number of repetitions in
trying to decode JSON data:

decodeTemperatureInfo : String -> TemperatureInfo


decodeTemperatureInfo json =
let
name =
Result.withDefault "Error decoding data!" (Decode

windSpeed =
Result.withDefault 0 (Decode.decodeString (Decode

temperature =
Result.withDefault 0 (Decode.decodeString (Decode

pressure =
Result.withDefault 0 (Decode.decodeString (Decode
humidity =
Result.withDefault 0 (Decode.decodeString (Decode
in
TemperatureInfo name windSpeed temperature pressure humid

Basically, what’s happening in the preceding code


is—we are decoding the JSON string we received
from the remote server, step by step. At this point,
saving and running our app, we would see exactly
the same window as before: Everything will go
here, this time using Html.program.
That is a good sign. It means our app compiles
successfully. It also means that there is more work
to be done in the view and main functions.
Updating the view
Our view function is there to give us the visual
representation of our model. For our app to work,
the view function needs to have an input field, the
submit button, and several values to be populated
upon contacting the remote server and decoding
the JSON string:

view model =
div []
[ input [ placeholder "City", onInput CityInput ] []
, br [] []
, button [ onClick GetTemp ] [ text "Get temperature"
, br [] []
, div [] [ text "Name: ", text model.temperatureInfo.
, div [] [ text "Temp: ", text (toString model.temper
, div [] [ text "Wind: ", text (toString model.temper
, div [] [ text "Pressure: ", text (toString model.te
, div [] [ text "Humidity: ", text (toString model.te
]

Let’s look at how the preceding code works. We


start off with the wrapping div. Next, we have the
input function, which will have the placeholder word
City in the text input field. We also have a message
that this input function will emit, and the message is
of CityInput type.
Next, we have the br function, and then the button
function, which will emit the GetTemp message onClick.
The next significant function is the div function,
which will have the string of ''Name: '' plus the value
of model.temperatureInfo.name. Similarly, we follow it up
with other div functions that concatenate strings
together based on the values contained inside the
current model.
Save everything and run your app. The result will
be a fully working weather app that fetches a JSON
string from a remote server and properly displays
the result. To make the app fetch the data, it’s
enough to type a major city name into the input
field, and click the button.
Before wrapping up this section, let’s look at the
complete weather app code:

module Main exposing (..)

import Html exposing (..)


import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Http
import Json.Decode as Decode

main =
Html.program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}

type alias Model =


{ temperatureInfo : TemperatureInfo
, city : String
}

type alias TemperatureInfo =


{ name : String
, windSpeed : Float
, temperature : Float
, pressure : Float
, humidity : Float
}

init : ( Model, Cmd Msg )


init =
( Model (TemperatureInfo "Did not load" 0 0 0 0) ""
, Cmd.none
)
type Msg
= GetTemp
| CityInput String
| NewTemp (Result Http.Error String)

update : Msg -> Model -> ( Model, Cmd Msg )


update msg model =
case msg of
GetTemp ->
( model, getTemperature model.city )

NewTemp (Ok json) ->


let
newTemperatureInfo =
decodeTemperatureInfo json
in
( { model | temperatureInfo = newTemperatureI

NewTemp (Err _) ->


( model, Cmd.none )

CityInput city ->


( { model | city = city }, Cmd.none )

view : Model -> Html Msg


view model =
div []
[ input [ placeholder "City", onInput CityInput ] []
, br [] []
, button [ onClick GetTemp ] [ text "Get temperature"
, br [] []
, div [] [ text "Name: ", text (model.temperatureInfo
, div [] [ text "Temp: ", text (toString model.temper
, div [] [ text "Wind: ", text (toString model.temper
, div [] [ text "Pressure: ", text (toString model.te
, div [] [ text "Humidity: ", text (toString model.te
]

subscriptions : Model -> Sub Msg


subscriptions model =
Sub.none

getTemperature : String -> Cmd Msg


getTemperature city =
let
url =
"http://api.openweathermap.org/data/2.5/weather?q
in
Http.send NewTemp (Http.getString url)

decodeTemperatureInfo : String -> TemperatureInfo


decodeTemperatureInfo json =
let
name =
Result.withDefault "Error decoding data!" (Decode

windSpeed =
Result.withDefault 0 (Decode.decodeString (Decode

temperature =
Result.withDefault 0 (Decode.decodeString (Decode

pressure =
Result.withDefault 0 (Decode.decodeString (Decode

humidity =
Result.withDefault 0 (Decode.decodeString (Decode
in
TemperatureInfo name windSpeed temperature pressure h
Summary
In this chapter, we have covered a lot of new
concepts, and reinforced some that we have
learned about in the previous chapters. We have
looked at using Result and Maybe to deal with
operations that might fail and to deal with possibly
missing data. We looked at using decoders and
mapping over them.
We also looked at using the Http package to get
remote JSON data. Now that we have set up the
basics of our weather app, we will discuss ways of
improving it in the next chapter.
Adding More Features
to the Weather App
Welcome to Chapter 8, Adding More Features to the
Weather App. The goal of this chapter is to
improve our simple weather app by adding more
features to it.
The topics we will cover include:

Adding elm-mdl to our app

Converting the temperature display from


Kelvin to Celsius

Working with the Round module

After completing this chapter, you will:

Be able to use the Material Design library


for Elm, elm-mdl

Understand how the Round module works


Importing Material and
Round modules
To start, let’s create a new folder, call it chapter8,
point the console inside this newly-created folder,
and run the following command:
create-elm-app improved-weather-app-ch8

Once the app is prepared, simply copy all the code


we had in Main.elm at the end of Chapter 7, Making a
Weather App in Elm, into this new Main.elm. This will
be our starting point (rather than beginning with
the boilerplate app, like we did in the last few
chapters).
However, there is a slight issue that we need to
resolve. Currently, our new app will not compile.
Instead, we will get the following warning:
Failed to compile
./src/Main.elm

I cannot find module 'Http'.

Module 'Main' is trying to import it.

Potential problems could be:


* Misspelled the module name
* Need to add a source directory or new dependency to elm-p

The issue stems from the fact that we did not


update the Elm packages. Let’s quickly fix that by
running the following command:
elm package install elm-lang/http 1.0.0
After we approve the upgrades, we’ll be greeted
with the following message output to the console:
Packages configured successfully!

Now we can start serving our Elm app and watch


for changes as we update our app. To start serving
the app, let’s point our console to the improved-weather-
app-ch8 folder and run the following command:

elm-app start

We will begin adding improvements to our app by


using the elm-mdl module. The preview of this
module in action is available at https://debois.github.i
o/elm-mdl/ and the official documentation can be
found at https://github.com/debois/elm-mdl.
To start off, let’s import all the dependencies we
will be using. Open Main.elm, and locate the
beginning section of the code, where the imports
are:
module Main exposing (..)

import Html exposing (..)


import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Http
import Json.Decode as Decode

At the bottom of the imports, we will be importing


elm-mdl module dependencies, and then, at the very
bottom, the Round package:

import Material
import Material.Button as Button
import Material.Card as Card
import Material.Color as Color
import Material.Elevation as Elevation
import Material.Options as Options
import Material.Scheme
import Material.Textfield as Textfield
import Material.Typography as Typography
import Round exposing (..)

The official elm-mdl package is also listed on the elm


packages website, at http://package.elm-lang.org/packages/
debois/elm-mdl/latest, and the Round package is located
at http://package.elm-lang.org/packages/myrho/elm-round/latest.
Unfortunately, at this point our app is again not
compiling. The reason is similar to the issue we
fixed a bit earlier: we need to install the missing
packages. We can verify that that is the case by
looking at the compiler’s error message:
Failed to compile
./src/Main.elm

I cannot find module 'Material'.

Module 'Main' is trying to import it.

Potential problems could be:


* Misspelled the module name
* Need to add a source directory or new dependency to elm-p

To fix the issue, let’s run the following command in


our console:

elm package install debois/elm-mdl

Next, let’s try running our app again, with elm-app


start.

Again, our app does not compile; this time, we are


missing the Round module. Let’s add it with the
following command in the console:

elm package install myrho/elm-round


After successfully installing the dependencies
needed to run our app, we can now see it
successfully compiling and being served to the
browser by running elm-app start once again. Right
now, the app looks exactly as it did at the end of Chap
ter 7, Making a Weather App in Elm.
Adding elm-mdl to our
Model
While our main function will remain unchanged—
meaning it will still use Html.program in exactly the
same way as in the previous chapter, we will have
to add changes to our Model type alias.
In Chapter 7, Making a Weather App in Elm, our Model
looked as follows:
type alias Model =
{ temperatureInfo : TemperatureInfo
, city : String
}

The only thing we need to do is add another entry


to the Model‘s record, as follows:

type alias Model =


{ temperatureInfo : TemperatureInfo
, city : String
, mdl : Material.Model
}

This new entry, mdl : Material.Model, is the type of data


that holds everything we need to display the mdl
element. Our TemperatureInfo type alias will remain
unchanged.
Updating the init
function
We will need to update the init function so that we
take into account the initial value of Material.model.
The init function we ended up with in Chapter 7,
Making a Weather App in Elm, looked as follows:

init : ( Model, Cmd Msg )


init =
( Model (TemperatureInfo "Did not load" 0 0 0 0) ""
, Cmd.none
)

The update is simple, and it will reflect the change


we made to the Model type alias:
init : ( Model, Cmd Msg )
init =
( Model (TemperatureInfo "Did not load" 0 0 0 0) "" Mater
, Cmd.none
)

With this update, we have sorted out the initial


values that our weather app needs.
Updating the Msg
union type and the
update function
In order to reflect the improvements we made with
the introduction of the elm-mdl package, we need to
add another type constructor to our Msg union type.
The Msg union type will look exactly as it did in Chapte
r 7, Making a Weather App in Elm, save for the
new Mdl type constructor, which we’ll simply
append to the end of the union type, as follows:

type Msg
= GetTemp
| CityInput String
| NewTemp (Result Http.Error String)
| Mdl (Material.Msg Msg)

The Mdl type constructor is simply the message that


will be generated by mdl.
Let’s now add improvements to our update function,
which looked like this at the end of Chapter 7,
Making a Weather App in Elm:

update : Msg -> Model -> ( Model, Cmd Msg )


update msg model =
case msg of
GetTemp ->
( model, getTemperature model.city )

NewTemp (Ok json) ->


let
newTemperatureInfo =
decodeTemperatureInfo json
in
( { model | temperatureInfo = newTemperatureI

NewTemp (Err _) ->


( model, Cmd.none )

CityInput city ->


( { model | city = city }, Cmd.none )

As we can see in the preceding code snippet, we


pattern-matched for GetTemp, NewTemp, and CityInput,
which was explained in Chapter 7, Making a Weather
App in Elm. The change that we need to make to
the update function at this point has to do with
pattern matching for the new Msg type constructor,
namely, Mdl.
Practically, this means that the update function will
remain exactly as it was, with just one simple
addition: Mdl branch for our case-of expression:
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Mdl msg_ ->
Material.update Mdl msg_ model

GetTemp ->
( model, getTemperature model.city )

NewTemp (Ok json) ->


let
newTemperatureInfo =
decodeTemperatureInfo json
in
( { model | temperatureInfo = newTemperatureInfo

NewTemp (Err _) ->


( model, Cmd.none )

CityInput city ->


( { model | city = city }, Cmd.none )

In the next section, we will add updates to our view


function.
Updating the view
function
The view function at the end of Chapter 7, Making a
Weather App in Elm, looked as follows:

view : Model -> Html Msg


view model =
div []
[ input [ placeholder "City", onInput CityInput ] []
, br [] []
, button [ onClick GetTemp ] [ text "Get temperature"
, br [] []
, div [] [ text "Name: ", text (model.temperatureInfo
, div [] [ text "Temp: ", text (toString model.temper
, div [] [ text "Wind: ", text (toString model.temper
, div [] [ text "Pressure: ", text (toString model.te
, div [] [ text "Humidity: ", text (toString model.te
]

Since we basically had no styles in the preceding


code, the updated view function might seem a bit
daunting at first glance:
view : Model -> Html Msg
view model =
div []
[ Textfield.render Mdl
[ 0 ]
model.mdl
[ Textfield.label "City"
, Textfield.floatingLabel
, Textfield.text_
, Textfield.value model.city
, Options.onInput CityInput
]
[]
, Button.render Mdl
[ 1 ]
model.mdl
[ Button.raised
, Button.colored
, Button.ripple
, Options.onClick GetTemp
]
[ text "Get temperature" ]
, br [] []
, Card.view
[ Options.css "width" "256px"
, Options.css "margin" "20px"
, Elevation.e8
, Elevation.transition 250
, Color.background (Color.color Color.LightBlue C
]
[ Card.title
[ Options.css "flex-direction" "column" ]
[ Card.head [] [ text model.city ]
, Options.div
[ Options.css "padding" "2rem 2rem 0 2rem
[ Options.span
[ Typography.display4
, Typography.contrast 0.87
, Color.text Color.primary
]
[ text (Round.round 0 model.temperatu
]
]
, Card.actions []
[ div [] [ text "Wind: ", text (toString mode
, div [] [ text "Pressure: ", text (toString
, div [] [ text "Humidity: ", text (toString
]
]
]
|> Material.Scheme.top

Check the look of your app in the browser, since at this


point we are successfully applying elm-mdl to it.

Luckily, the preceding code is pretty


straightforward once you understand how it works.
Let’s go through it step by step. Since the code in Ch
apter 7, Making a Weather App in Elm, did not have
any styles, the functions we used were easy enough
to understand from the get-go. We simply had an
input, a button, and four divs containing text nodes. As
we did not use any styles, we used br functions to
visually separate the input, the button, and the four
divs.
The improvements in Chapter 8, Adding More
Features to the Weather App, involve adding mdl
specific elements, including mdl specific Textfield,
Button, and Card. Let’s look at Textfield a bit closer.
Understanding the
Textfield function
The code for our Textfield is as follows:
Textfield.render Mdl
[ 0 ]
model.mdl
[ Textfield.label "City"
, Textfield.floatingLabel
, Textfield.text_
, Textfield.value model.city
, Options.onInput CityInput
]
[]

We start off with Textfield.render Mdl, which means


that its message type is Mdl and that it simply
renders the Textfield component. Every mdl
component that we use needs to have a unique ID,
simply marked with a number. In the preceding
example, we are assigning a unique ID to Textfield
using a zero: [ 0 ].
Next, we specify the part of the model that this will
apply to, which is model.mdl. In the second list, we
provide the specifics on the appearance we want
our Textfield to have.
All the options that have to do with appearance for
our Textfield component are available at the
following URL, http://package.elm-lang.org/packages/deboi
s/elm-mdl/8.1.0/Material-Textfield.

To understand how the appearance changes in the


second list provided to our Textfield, we can simply
turn off some of the members of the list, like so:
Textfield.render Mdl
[ 0 ]
model.mdl
[ --Textfield.label "City"
--, Textfield.floatingLabel
--, Textfield.text_
Textfield.value model.city
, Options.onInput CityInput
]
[]

By commenting out Textfield.label,


Textfield.floatingLabel, and Textfield.text_, we effectively
turned them off, leaving only the bare minimum of
the functionality needed for our app to still work. If
we were to comment out either Textfield.value
model.city, or Options.onInput CityInput, our app would

stop working, so in the preceding case, we had to


keep these two, while we were able to comment out
the rest.
The reason our app would stop working is that we
would break the model that the update function expects
from the view.

As mentioned earlier in this section, we can refer to


the Options, Appearance, and Html attributes
sections of the official documentation for Textfield in
order to get different results and styling. For
example, we could set the maxlength of our Textfield to,
say, five characters. In that case, our Textfield will
need to look as follows:
Textfield.render Mdl
[ 0 ]
model.mdl
[ Textfield.maxlength 5
, Textfield.text_
, Textfield.value model.city
, Options.onInput CityInput
]
[]
Now, we will be able to type a city whose name is
up to five characters long. For example, we can still
look for weather information for Paris, but we
would not be able to look up London, since we
would not be able to type all six characters of that
city’s name.
Next, we’ll go through the Button function.
Understanding the
Button function
Looking at the Button after the Textfield, we can see
the following code:

Button.render Mdl
[ 1 ]
model.mdl
[ Button.raised
, Button.colored
, Button.ripple
, Options.onClick GetTemp
]
[ text "Get temperature" ]

Armed with our understanding of how Textfield


works, we can figure out the preceding section of
our view function almost at a glance: our Button gets
rendered with an Mdl message. We give our Button an
id of 1, and we use the appearance options of
Button.raised, Button.colored, and Button.ripple. We top it

off by sending the GetTemp message, by using elm-


mdl’s Options.onClick. There is also a third list, which
reads:
[ text "Get temperature" ]

Why did we use an empty list in Textfield, if we were


not going to use it? The reason is, of course, to
satisfy Elm’s static type system. For example, if we
were to erase the last, empty list from the
Textfield function, the compiler would throw an
error. Next, we’ll look at the Card function.
Understanding the
Card function
As a component, Card is relatively complex, as it is
made up of several subsections, since its role is to
display related information in a coherent way. The
official documentation for the Card function is
available at the following URL, http://package.elm-lang.
org/packages/debois/elm-mdl/8.1.0/Material-Card.
Understanding the use
of Card.view
Let’s look at the first several lines where we are
using the Card function:

, br [] []
, Card.view
[ Options.css "width" "256px"
, Options.css "margin" "20px"
, Elevation.e8
, Elevation.transition 250
, Color.background (Color.color Color.LightBlue C
]

First thing to note, the br function is completely


redundant here, so we can simply erase it without
any changes in the functionality or layout of our
weather app. We begin the next line with Card.view.
As the official documentation reads, the Card.view
function is used to construct a card. While Textfield
and Button were constructed using the render
function, here we are using view. We add additional
styling using Options.css, followed with two Strings,
the former setting the property of our CSS, and the
latter setting the value for the property.
Material.Elevation is used to give the shadow effect to
our mdl components. For a full description of
available Elevations, refer to the official
documentation at http://package.elm-lang.org/packages/deb
ois/elm-mdl/8.1.0/Material-Elevation. For example, to get

the maximum possible shadow using Elevation, we


can change the line that reads Elevation.e8 to a new
value, Elevation.e24. Similar to shadow, we can set the
time it takes for our transition, in milliseconds. We
set our transition to a quarter of a second with
Elevation.transition 250.

The next line sets the color for our card:


, Color.background (Color.color Color.LightBlue Color.S50)

The official documentation of the elm-mdl package


that deals with Material.Color is available at http://packa
ge.elm-lang.org/packages/debois/elm-mdl/8.1.0/Material-Color.
Rendering the Card.title
content block
Next, we render a Card.title, as follows:
[ Card.title
[ Options.css "flex-direction" "column" ]
[ Card.head [] [ text model.city ]
, Options.div
[ Options.css "padding" "2rem 2rem 0 2rem
[ Options.span
[ Typography.display4
, Typography.contrast 0.87
, Color.text Color.primary
]
[ text (Round.round 0 model.temperatu
]
]

Before going through each of the preceding lines, it


is important to note that each Card in elm-mdl consists
of content blocks. Content blocks can be Card.title,
Card.media, Card.text, and Card.actions.

The type signature for the Card.title content block is


as follows:
title : List (Style a) -> List (Html a) -> Block a

This means that each title content block takes two


Lists and returns a Block.

All the other content blocks (media, text, and actions) have
identical type signatures, save for the name of the
content block function.

Since we know that the title content block needs to


have two Lists, and that the first List needs to
specify styles, it’s now easy to understand what this
line of code does:
[ Options.css "flex-direction" "column" ]

The preceding line of code simply specifies the style


to be used on our Card.title. If we wanted to, we
could, for example, add another style here:

[ Options.css "flex-direction" "column"


, Options.css "padding-left" "100px"
]

Obviously, with the preceding change, our card will


get a left padding of 100 pixels. The second List
inside our Card.title content block specifies the Html:
[ Card.head [] [ text model.city ]
, Options.div
[ Options.css "padding" "2rem 2rem 0 2rem
[ Options.span
[ Typography.display4
, Typography.contrast 0.87
, Color.text Color.primary
]
[ text (Round.round 0 model.temperatu
]
]

In the first line, we can see Card.head being used. The


official documentation for Card.head is available at htt
p://package.elm-lang.org/packages/debois/elm-mdl/8.1.0/Material
-Card#head. The Card.head function behaves as we expect
—it takes two Lists and returns an Html a. The first
List lets us specify Style, and the second one lets us
specify Html.
Looking at the official documentation for
Material.Options, available at http://package.elm-lang.org/pac
kages/debois/elm-mdl/8.1.0/Material-Options, we can
navigate to the Elements section, which begins with
div. As the official documentation reads, div is a:

“Convenience function for the ultra-common case of apply elm-mdl


styling to a div element.”
Inside Options.div, we specify the styles in the first
List, then Html with Options.span in the second
List. Following the same pattern, the Options.span

itself holds two Lists. Inside the first List we specify


the styles to use by calling Material.Typography and
Material.Color—all together three separate styles.
Then, in the second List, expectedly, we render a
text node.

The contents of the text node, inside parentheses,


might look a bit complicated, so let’s pick it apart.
To begin with, we can see that we are calling
Round.round. Let’s refer to the official documentation
for more information on this package. To do that,
point your browser to the following URL, http://packa
ge.elm-lang.org/packages/myrho/elm-round/latest/Round.

As it says on the official page of the Round package, it


allows us to convert a Float to a String, with the
additional benefit of setting the number of digits
after the decimal point. It also lets us specify how
we want to round the rest of the digits on our Float.
The first example we see on the official page is this
one:

x = 3.141592653589793

round 2 x -- "3.14"

The preceding code shows that round takes an Int and


a Float, and returns a String. You can find the exact
type signature at the following link, http://package.elm-
lang.org/packages/myrho/elm-round/latest/Round#round.

In our case, we have this code:

Round.round 0 model.temperatureInfo.temperature ++ "°"

Looking at the temperatureInfo type alias, we can see


that temperatureInfo.temperature is a Float. So, we are
indeed taking an Int and a Float, and returning a
String. One caveat here is the concatenated degree
symbol, which, although a String, gets concatenated
with a Float, all thanks to Round.round.
The value of the first parameter is zero, meaning
we do not want to have any decimal points when
displaying the temperature. We can change that to,
for example, one digit after the decimal point, by
simply replacing the zero, like so:
Round.round 1 model.temperatureInfo.temperature ++ "°"

Next, we’ll look at the Card.actions code.


Understanding the
Card.actions code
Finally, it’s time to discuss the Card.actions part of
our Card component:

, Card.actions []
[ div [] [ text "Wind: ", text (toString model.temperatur
, div [] [ text "Pressure: ", text (toString model.temper
, div [] [ text "Humidity: ", text (toString model.temper
]

The official documentation of Material.Card specifies


Card.actions as allowing us to generate an actions

block. As we have seen before, the actions block is


simply one of the four possible content blocks for
our mdl components.
The official documentation for the Material Design
Language—that is, Google documentation, not Elm
package documentation—states that a card action
is to be used as a way to interact with our card. In
essence, it’s a call to an action button. For example,
if we displayed a listing of blog post excerpts in the
form of MDL cards on a website, the card action
could be the button that holds the Read More text
node.
However, in our example, we are not really
interacting with the information provided under
the temperature in our card component. In other
words, we do not have the intention for the user to
click on the wind, pressure, and humidity
information that is listed below the temperature
reading.
Therefore, it is safe to change the line that reads ,
Card.actions [] with this code: , Card.text []. We can

leave all the other code as is, and the app will still
work. The only change that will happen has to do
with the compiled HTML structure of our weather
app. In order to see the change, we will need to
inspect the code in the dev tools, which can be
accessed in most major browsers by pressing the
F12 button.
If using Card.action in our Elm app, the resultant div
in the browser will have this CSS class attribute:
mdl-card__actions. If we instead switch to using Card.text
in our view, the resultant div of the running app in
the browser will have the CSS class attribute of mdl-
card__supporting-text.

We finish our view function’s code by piping the


Material.Scheme.top at the very bottom. This will add

the mdl CSS to our app. To find out more about this,
refer to the official documentation at http://package.el
m-lang.org/packages/debois/elm-mdl/8.1.0/Material-Scheme#top.
Adding color schemes
We can change the way our mdl-styled app looks,
almost effortlessly, by utilizing color schemes. To
understand how to do that, we will first need to
refer to the official documentation of the elm-mdl
package. More specifically, we are interested in
topWithScheme, a function that is made available in the

elm-mdl package. The official documentation for this


function is available at http://package.elm-lang.org/packag
es/debois/elm-mdl/8.1.0/Material-Scheme#topWithScheme. If you
visit the referenced URL, you will see that all we
need to do to make it work is provide the primary
and accent colors as parameters to the topWithScheme
function.
To better understand how the color schemes work in
mdl, refer to the color scheme customizer at
https://getmdl.io/customize/index.html.

Practically, this means that we can update our view


function with a number of color schemes, just by
changing one line of code. Until now, the first few
lines of our view function looked as follows:

view : Model -> Html Msg


view model =
div []
[ Textfield.render Mdl

To update the view so that it uses the topWithScheme


function, we simply add the following:

view : Model -> Html Msg


view model =
div []
[ Material.Scheme.topWithScheme Color.Orange Color.Re
Textfield.render Mdl

As we can see, the primary color is now orange, and


the accent color is red. At this point, we have nearly
finished updating our weather app. There are no
changes to be made to either the subscriptions
function or to the getTemperature function.
The only function that still needs updating is the
decodeTemperatureInfo function, in which we need to
make just some minor tweaks, which we will be
doing in the next section.
Updating
decodeTemperatureInfo
We left off in Chapter 7, Making a Weather App in
Elm, with the following code for decodeTemperatureInfo:

decodeTemperatureInfo : String -> TemperatureInfo


decodeTemperatureInfo json =
let
name =
Result.withDefault "Error decoding data!" (Decode

windSpeed =
Result.withDefault 0 (Decode.decodeString (Decode

temperature =
Result.withDefault 0 (Decode.decodeString (Decode

pressure =
Result.withDefault 0 (Decode.decodeString (Decode

humidity =
Result.withDefault 0 (Decode.decodeString (Decode
in
TemperatureInfo name windSpeed temperature pressure h

The only thing to update in the decodeTemperatureInfo


function is that we need to convert the temperature
from Kelvin to Celsius. Luckily, this conversion is
very straightforward: in order to convert to Celsius,
we simply need to subtract 273.15 from the Kelvin-
based value. In other words, if the current
temperature is 293.15 Kelvins, the conversion to
Celsius will look as follows:
293.15 - 273.15 = 20
Thus, 293.15 degrees Kelvin is 20 degrees Celsius.
This makes it pretty straightforward to update our
decodeTemperatureInfo. We simply need to replace the
existing code with this one:
temperature =
Result.withDefault 0 (Decode.decodeString (Decode.at [ "

We will leave the rest of the code the same as in the


previous version of our app.
In this section, we updated the temperature so as
to display Celsius to Kelvin. If you view the app in
the browser, you will be able to see this change.
Summary
In this chapter, we have learned how to improve
the styling of an existing app with the wonderful
elm-mdl package. We have looked at using Result and

Maybe to deal with operations that might fail and to

deal with possibly missing data. We looked at using


decoders, and mapping over them. We also looked
at using the HTTP package to get remote JSON
data.
In the next chapter, we will look at how to write
tests for our Elm apps.
Testing in Elm
In this chapter, we will learn how to test our Elm
apps.
The topics we will cover include:

Testing Elm apps with elm-test

Understanding the structure and the role of


the elm-package.json file

Understanding the structure and


functionality of the tests folder

Working with describe, test, and Expect

Writing easier-to-understand tests with the


left pipe operator

Using the let-in and case-of expressions in


our tests

Fuzz testing in Elm

After completing this chapter, you will:

Have a general idea of how unit testing


works

Understand how unit testing and fuzz


testing works in Elm
Understand different ways in which you can
test your Elm apps

Be able to successfully deploy a variety of


tests in your Elm apps
Introduction to Elm
testing
Since the compiler catches all errors at
compilation, the highly-touted zero runtime
exceptions is the expected outcome for Elm apps
that compile successfully.
Since that is the case, one might ask whether we
need to test our Elm apps at all. The answer is a
resounding yes, primarily because the compiler will
not test the behavior of the app. While the Elm
compiler’s error-checking is indeed a wonderful
tool, it will only test for logical inconsistencies, and
that is pretty much it.
To prepare for this chapter, we’ll start with the
code from the previous chapter. Simply copy the
entire folder titled improved-weather-app-ch8 and paste it
in another location.
The code files that come with this book have this
pasted folder inside chapter9, and the copy-pasted
folder that can be found inside the chapter9 folder
has been renamed to weather-app-with-tests-ch9.
It’s sometimes helpful to simply copy and paste an
existing Elm project into a new folder and then change
it, rather than create it from scratch with, for example,
the create-elm-app package.
Understanding how
testing works in Elm
We’ll begin working with testing in Elm by pointing
our console to the weather-app-with-tests-ch9 folder.
Once we have our terminal pointed to the proper
directory, we will need to install elm-test as an npm
package. There are a number of utilities used in the
Elm community that have their npm versions, and
elm-test is just one of them.

Thus, to install elm-test, let’s run:

npm install -g elm-test

That’s it! The elm-test npm package is now available


globally, because we passed the -g flag to the
command. What this means is we can use elm-test
in any folder, not just the one we are currently in.
Note that the installation of the elm-test npm package
will take some time, so feel free to take a break while it
is being installed.

Once the installation gets going, a number of


messages will be logged to the console. These
messages will look similar to the following:
C:\Users\PC\AppData\Roaming\npm\elm-test -> C:\Users\PC\AppDa

> elm-test@0.18.12 install C:\Users\PC\AppData\Roaming\npm\no


> node install.js

Downloading binaries from https://dl.bintray.com/elmlang/elm-


Successfully downloaded and processed https://dl.bintray.com/
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.1.
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported pla
+ elm-test@0.18.12
added 166 packages in 649.159s

Note the last line of the preceding code, which


reads:

added 166 packages in 649.159s

This means that the installation took a bit over 10


minutes, which is a perfect opportunity to make
yourself a nice beverage while waiting for the
installation to complete. Once the installation of
elm-test is complete, we can add the tests folder to
our existing elm app simply by running the
following command:
elm-test init

Once the preceding command gets run in the


console, the console will record the progress by
logging the following messages:
Starting downloads...

● debois/elm-dom 1.2.3
● eeue56/elm-html-query 3.0.0
● eeue56/elm-html-in-elm 5.2.0
● eeue56/elm-lazy 1.0.0
● eeue56/elm-lazy-list 1.0.0
● eeue56/elm-html-test 5.1.3
● eeue56/elm-shrink 1.0.0
● elm-community/elm-test 4.2.0
● elm-lang/core 5.1.1
● debois/elm-mdl 8.1.0
● elm-lang/dom 1.1.1
● elm-lang/html 2.0.0
● elm-lang/http 1.0.0
● elm-lang/mouse 1.0.1
● elm-lang/virtual-dom 2.0.4
● elm-lang/window 1.0.1
● mgold/elm-random-pcg 5.0.2
● myrho/elm-round 1.0.2
Packages configured successfully!

Let’s run the dir command to see the structure of


our project, from its root:
dir

This command will return:


elm-package.json elm-stuff public README.md src tests

Now, let’s change to the tests folder by running cd


tests. Next, let’s inspect the structure of the tests

folder by running the dir command again. What we


get back is the following:
elm-package.json elm-stuff Example.elm Tests.elm

The first file in the list, elm-package.json, lists all the


dependencies that are needed for our tests. Here is
the content of the elm-package.json file that’s located
inside the tests folder:
{
"version": "1.0.0",
"summary": "Test Suites",
"repository": "https://github.com/user/project.git",
"license": "BSD3",
"source-directories": [
"..\\src",
"."
],
"exposed-modules": [],
"dependencies": {
"debois/elm-mdl": "8.1.0 <= v < 9.0.0",
"eeue56/elm-html-test": "5.1.3 <= v < 6.0.0",
"elm-community/elm-test": "4.0.0 <= v < 5.0.0",
"elm-lang/core": "5.0.0 <= v < 6.0.0",
"elm-lang/html": "2.0.0 <= v < 3.0.0",
"elm-lang/http": "1.0.0 <= v < 2.0.0",
"myrho/elm-round": "1.0.2 <= v < 2.0.0"
},
"elm-version": "0.18.0 <= v < 0.19.0"
}

The following lines from the preceding code are


used for the semantic versioning of our package:
"version": "1.0.0",
"summary": "Test Suites",
"repository": "https://github.com/user/project.git",
"license": "BSD3",

These lines are only relevant if you are going to


publish a package on the Elm package page. Source
directories lists the folders where our source files
are located. In the case of our tests folder, it needs
to have access to itself, signified by the ., and it
needs to have access to its parent folder’s src folder,
signified by ..\\src:

"source-directories": [
"..\\src",
"."
],

Exposed modules list all the modules that you want


to expose to the public. This is only used when
publishing a package and it does not need to be
specified otherwise.
Dependencies lists all the packages that our tests
depend on. It is interesting to compare the list of
dependencies from the root of our weather app’s
folder with the list of dependencies that our tests
are using. To compare the two, let’s begin by listing
the dependencies from the weather app’s folder:
"dependencies": {
"debois/elm-mdl": "8.1.0 <= v < 9.0.0",
"elm-lang/core": "5.0.0 <= v < 6.0.0",
"elm-lang/html": "2.0.0 <= v < 3.0.0",
"elm-lang/http": "1.0.0 <= v < 2.0.0",
"myrho/elm-round": "1.0.2 <= v < 2.0.0"
},

Next, let’s list the dependencies inside the test


folder:
"dependencies": {
"debois/elm-mdl": "8.1.0 <= v < 9.0.0",
"eeue56/elm-html-test": "5.1.3 <= v < 6.0.0",
"elm-community/elm-test": "4.0.0 <= v < 5.0.0",
"elm-lang/core": "5.0.0 <= v < 6.0.0",
"elm-lang/html": "2.0.0 <= v < 3.0.0",
"elm-lang/http": "1.0.0 <= v < 2.0.0",
"myrho/elm-round": "1.0.2 <= v < 2.0.0"
},

As we can see, the tests folder includes all the


dependencies used by our weather app, plus some
additional dependencies. Since we already know
what the packages inside the weather app’s src
folder do, let’s focus on those that are specific to
our tests folder:
"eeue56/elm-html-test": "5.1.3 <= v < 6.0.0",
"elm-community/elm-test": "4.0.0 <= v < 5.0.0",

As we can see, there are two packages that are


specifically used in our tests folder. Before looking
into what each of them does, let’s briefly discuss
package naming conventions, as well as our
dependencies’ versions as they were listed
previously.
The way that Elm package naming works is simple.
The part of the package name before the slash is
the name of the GitHub account associated with
the package. In other words, it’s the GitHub
username of the package author. The part of the
package name after the slash should be as
descriptive as possible. So, rather than enforcing
unique naming for each package, Elm’s philosophy
is to have as descriptive a name as possible for a
package, which will allow its potential users to
discern the purpose of the package just by looking
at its name. Thus, it’s entirely possible that there is
more than one elm-html-test package, and the
uniqueness of the name is determined by the
username of the package author.
The versions required for our eeue56/elm-html-test
dependency are in a range between 5.1.3 and 6.0.0,
meaning, that the lowest acceptable version is 5.1.3
and the highest acceptable version is 6.0.0.
To understand what these two packages do, we can
refer to their official documentation, which is
available at http://package.elm-lang.org/packages/elm-communi
ty/elm-test/latest and http://package.elm-lang.org/packages/ee
ue56/elm-html-test/latest.

The elm-community/elm-test package allows us to write


unit tests and fuzz tests. eeue56/elm-html-test allows us
to test views by specifying the HTML values that we
expect.
Continuing to inspect the contents of the tests
folder, the tests/elm-stuff folder contains the
downloaded packages and the list of exact versions
of our dependencies, as listed in exact-
dependencies.json. Next, the Example.elm file has all the
necessary setup for us to run our first test. This is
the code that our Example.elm file contains:

module Example exposing (..)

import Expect exposing (Expectation)


import Fuzz exposing (Fuzzer, int, list, string)
import Test exposing (..)

suite : Test
suite =
todo "Implement our first test. See http://package.elm-la
The code in this file follows the same rules as any
other Elm code. On the first line, we are exposing
the Example module. We follow it up with importing
Expect, Fuzz, and Test, which are all needed for our
tests to work.
It is helpful to be familiar with some of the jargon
of software testing, as it will give some more
context to our test writing. Tests are grouped into
test suites. A test suite holds test cases. A test case
is the smallest test unit. Therefore, appropriately,
we start our testing in Example.elm by specifying a suite
function, which will hold our tests. As we can see,
the suite function is of type Test. The official
documentation on Test can be found at http://package.
elm-lang.org/packages/elm-community/elm-test/latest/Test#Test.

As the official documentation reads, a Test will


produce at least one Expectation. Let’s look at some
other functions we’ll use in our tests.
The describe function
Our tests are grouped inside a List. To describe
what this List of tests is doing, we use the describe
function. The signature of the describe function can
be found at this link http://package.elm-lang.org/packages/e
lm-community/elm-test/latest/Test#describe

As we can see from the URL provided, the describe


function takes a String and a List of Tests, and returns
a Test. In other words, it follows this kind of
structure:

describe "An arbitrary description of our test"


[ test ...
, test ...
, test ...
]

Note that this is not the actual Elm code, but rather
an Elm-like pseudocode, written as an intermediate
step to better understand tests in Elm.
The test and Expect
functions
The test function is used to write the actual unit
test. The test function can have only one Expectation,
and returns a Test.
Expectation is a type alias. It can be one of the two: a
pass or a fail. Expect has several functions, such as
Expect.equal, Expect.notEqual, Expect.lessThan, and so on.

For the full list of Expect functions, refer to the


official documentation at http://package.elm-lang.org/pac
kages/elm-community/elm-test/latest/Expect.

Now that we have covered some of the concepts


and described the functions we will use, it’s time to
write our first unit test inside Example.elm.
Writing unit tests in
Elm
Let’s update the code of our suite function, so that it
looks like this:

suite : Test
suite =
describe "Zero is equal to zero"
[ test "Zero is equal to one minus one" <|
\_ -> Expect.equal 0 (1 - 1)
, test "Zero is equal to two minus two" <|
\_ -> Expect.equal 0 (2 - 2)
]

Let’s now run this test from the console, by


pointing the console to the root of our project (one
level up from the test folder), and typing the
following command:

elm-test tests/Example.elm

The console will log the following message:

$ elm-test tests/Example.elm
Success! Compiled 0 modules.
Successfully generated /dev/null
Success! Compiled 1 module.
Successfully generated C:\Users\PC\Desktop\improved-weather-a

elm-test 0.18.12
----------------

Running 2 tests. To reproduce these results, run: elm-test --

TEST RUN PASSED

Duration: 517 ms
Passed: 2
Failed: 0

Let’s look at a single test case from the code that we


have just tested:

test "Zero is equal to one minus one" <|


\_ -> Expect.equal 0 (1 - 1)

Let’s start with <| pipe operator. What it does is


evaluate the expression that can be found on its
right-hand side, and passes it as an argument to
the function on its left-hand side. In other words,
to write the exact same text function without using
the <| operator, we could have written it like this:

test "Zero is equal to one minus one"


( \_ -> Expect.equal 0 (1 - 1) )

Next, let’s look inside the parentheses and describe


what is happening there. The code inside the
parentheses is the second argument of the test
function:

( \_ -> Expect.equal 0 (1 - 1) )

This second argument is an anonymous function


which ignores its parameter, which is signified by
this bit of code: \_. Earlier in the book, we discussed
how every function in Elm is curried, and how,
using partial application, we can conclude that each
function in Elm can be made to take only one
argument. With \_, that one argument is ignored.
The arrow (->) in anonymous functions is the same
as the equals sign (=) in regular functions. The
Expect.equal will, as mentioned, return either a pass or
a fail.
takes two arguments: the first one is the
Expect.equal
expected value, and the second one is the
expression that will be tested and that will either be
the same as the expected value (so a pass gets
returned), or not (so a fail gets returned).
Since zero is indeed equal to one minus one, we got
a pass.
Going back to using the <| pipe operator, we could
have written this anonymous function as follows:
( \_ -> Expect.equal 0 <| 1 - 1 )

Thus, we could, if we wanted to, rewrite the entire


suite function without any parentheses, like this:

suite : Test
suite =
describe "Zero is equal to zero"
[ test "Zero is equal to one minus one" <|
\_ -> Expect.equal 0 <| 1 - 1
, test "Zero is equal to two minus two" <|
\_ -> Expect.equal 0 <| 2 - 2
]

Everything will still work as expected, and our


tests, if we ran them again, would still pass.
Let’s now divert our attention to the other test file
in the tests folder, called Tests.elm. The Tests.elm file
has the following code:

module Tests exposing (..)

import Test exposing (..)


import Expect

-- Check out http://package.elm-lang.org/packages/elm-communi

all : Test
all =
describe "A Test Suite"
[ test "Addition" <|
\_ ->
Expect.equal 10 (3 + 7)
, test "String.left" <|
\_ ->
Expect.equal "a" (String.left 1 "abcdefg")
, test "This test should fail" <|
\_ ->
Expect.fail "failed as expected!"
]

Let’s run the test in our console by simply running


the elm-test command from the root of our app’s
folder:

elm-test

The following information will be logged to the


console:
Success! Compiled 1 module.
Successfully generated /dev/null
Success! Compiled 1 module.
Successfully generated C:\Users\PC\Desktop\improved-weather-a

elm-test 0.18.12
----------------

Running 5 tests. To reproduce these results, run: elm-test --

> Tests
> A Test Suite
> This test should fail

failed as expected!

TEST RUN FAILED

Duration: 696 ms
Passed: 4
Failed: 1
This test failed, but as the console output reads,
that was expected. As can be seen in the code of the
Tests.elm file, when we need to write a failing test, we
simply use the Expect.fail function.
As we will continue working with the Tests.elm file in
the following sections of this chapter, a useful
command that we can use is elm-test --watch. If you
are familiar with a number of different utilities that
can be run in the console, the --watch flag watches
for changes in our code and reruns the command
that the --watch flag is appended to.
This means that we’ll only need to run the elm-test --
watch command once, and every time we save the

Tests.elm file, it will run our test suite again.


Using let-in
expressions in our
tests
Let’s add a let-in expression to our Example.elm file. To
do that, we’ll specify another test suite by using
another describe function, and then we’ll wrap all
our tests in yet another describe function. This
outermost describe function will contain all the other
describe functions, which will, in turn, hold all the
Lists of test functions:

module Example exposing (..)

import Expect exposing (Expectation)


-- import Fuzz exposing (Fuzzer, int, list, string)
import Test exposing (..)

suite : Test
suite =
describe "A Test Suite"
[ describe "Testing addition"
[ test "Addition" <|
\_ ->
Expect.equal 10 (3 + 7)
]
, describe "Using let-in expression in a test suite"
[ test "Multiplication" <|
\_ ->
let
x = 2
y = 4
xy = x * y
in
xy
|> \_ -> Expect.equal 8 (2 * 4)
]
]
Running this Example.elm file with elm-test
tests/Example.elm will produce the following output to

the console:
Success! Compiled 1 module.
Successfully generated /dev/null
Success! Compiled 1 module.
Successfully generated C:\Users\PC\Desktop\improved-weather-a

elm-test 0.18.12
----------------

Running 2 tests. To reproduce these results, run: elm-test --

TEST RUN PASSED

Duration: 334 ms
Passed: 2
Failed: 0

Of course, this example is trivial, but it is helpful to


see a very simple implementation that you can
build from.
Decoding JSON in our
tests
Decoding JSON in our tests is simple.
To begin, let’s add a new file inside our tests folder.
We’ll call this new file DecoderTests.elm.
Next, let’s add the following code to DecoderTests.elm:

module DecoderTests exposing (..)

import Expect exposing (Expectation)


import Fuzz exposing (Fuzzer, int, list, string)
import Main exposing (..)
import Test exposing (..)

suite : Test
suite =
describe "Decoder test"
[ test "Test decoding valid string" <|
\_ ->
let
jsonInput =
"{\"main\":{\"temp\":303.15,\"pressur

expectedResult =
TemperatureInfo "Rome" 15.3 30.0 30.0
in
Expect.equal expectedResult (decodeTemperatur

{--
, test "Test decoding invalid string" <|
\_ ->
let
jsonInput =
"Scrabbled json message"

expectedResult =
TemperatureInfo "Error decoding data!
in
Expect.equal expectedResult (decodeTemperatur
, fuzz string "Fuzz test decoding invalid string" <|
\randomlyGeneratedString ->
let
expectedResult =
TemperatureInfo "Error decoding data!
in
Expect.equal expectedResult (decodeTemperatur
-}
]

Now we can run our tests:


elm-test tests/DecoderTests.elm

The console will log the following information:


Success! Compiled 2 modules.
Successfully generated /dev/null
Success! Compiled 3 modules.
Successfully generated C:\Users\PC\Desktop\elm-web-developmen

elm-test 0.18.12
----------------

Running 1 test. To reproduce these results, run: elm-test --f

TEST RUN PASSED

Duration: 663 ms
Passed: 1
Failed: 0

The code we wrote to test this JSON has a total of


three tests. The first test is written so that it doesn’t
fail. The other two are written to fail, but they are
commented out. Feel free to un-comment the other
two tests and see the result in the console.
In order to be able to parse JSON properly, we
need to escape double quotes in the JSON string.
That’s why the jsonInput variable looks like this:

jsonInput =
"{\"main\":{\"temp\":303.15,\"pressure\":30,\"humidity\":20},
An alternative approach would be to enclose the
entire JSON string inside the opening and closing
multiline quotes, which, as mentioned earlier in the
book, are groupings of three double quotes, like
this: """. We will see an example of this approach in
the next section, when we build some custom
expectations.
Building custom
expectations while
decoding JSON
To build custom expectations, we can refer to the
previously-mentioned pass and fail functions. The
pass function will always pass, and the fail function
will fail with a message. To produce a custom
message, you can use the onFail function.
Let’s see an example by creating a new file inside
the tests folder. We’ll call this new
file CustomExpectations.elm and add the following code:
module CustomExpectations exposing (..)

import Expect exposing (Expectation)


import Json.Decode exposing (decodeString, field, int, list,
import Test exposing (..)

type alias Player =


{ name : String
, language : String
}

playerDecoder =
map2 Player
(field "name" string)
(field "language" string)

suite : Test
suite =
describe "A Test Suite"
[ describe "A custom expectation with a custom decode
[ test "Decoding JSON strings" <|
\_ ->
let
json =
"""
{
"name" : "John Doe",
"language" : "English"
}
"""
in
case decodeString playerDecoder json of
Ok json ->
Expect.pass

Err err ->


Expect.fail err
]
]

If we ran this test with the command elm-test


tests/CustomExpectations.elm, we would get the following

output:
Success! Compiled 0 modules.
Successfully generated /dev/null
Success! Compiled 1 module.
Successfully generated C:\Users\PC\Desktop\elm-web-developmen

elm-test 0.18.12
----------------

Running 1 test. To reproduce these results, run: elm-test --f

TEST RUN PASSED

Duration: 468 ms
Passed: 1
Failed: 0

In the code, we are setting the type alias Player in


the global scope so that we can access it in our tests
when needed. The Person type alias is a record that
has the name field, which is a String, and the language
field, with is also a String.
We use the describe function to describe our suite of
tests, and then we describe the first List of tests,
which actually holds only one test right now.
Let’s look at the playerDecoder function:
playerDecoder =
map2 Player
(field "name" string)
(field "age" string)

As we learned in Chapter 6, Exploring Elm in Greater


Detail, string is a decoder with the following
signature:

decoder : Json.Decode.Decoder String

So, string is a decoder of Strings, meaning it


translates JSON strings into Elm Strings, and this
process of parsing JSON strings into Elm Strings is
called decoding.
The expression field "name" string is also a decoder of
Strings, and the entire playerDecoder is a complex
decoder that is composed of the field "name" string
decoder and the field "age" string decoder.
Inside the anonymous function, we provide the let-
in expression. Inside the let portion of the let-in
expression, we define our json string.
Finally, inside the in portion of the let-in
expression, we evaluate a case-of expression, and
with the help of the pass and fail functions, we write
our custom expectations.
Next, we will look at fuzz testing.
Writing fuzz tests in
Elm
Fuzz tests allow us to take our unit tests to a new
level by enabling us to run an arbitrary number of
combinations of values in our tests, by virtue of
randomizing some of the inputs in our tests.
In order to see how fuzz tests work, let’s compare
unit tests with fuzz tests.
Let’s begin with the regular unit test that we have
already worked with earlier in this chapter. To
refresh our memory of this test, let’s add it as the
contents of Example.elm, and then run the actual test:
module Example exposing (..)

-- import Fuzz exposing (Fuzzer, int, list, string)

import Expect exposing (Expectation)


import Test exposing (..)

suite : Test
suite =
describe "Zero is equal to zero"
[ test "Zero is equal to one minus one" <|
\_ -> Expect.equal 0 (1 - 1)
, test "Zero is equal to two minus two" <|
\_ -> Expect.equal 0 (2 - 2)
]

Note that, in this code, the Fuzz import is


commented out, since the compiler would give us a
warning about this import not being used.
If we ran this test, it would log a successful test
message to the console, with the information that
two tests have passed and zero have failed.
Although the example is very simplistic, it will help
us compare unit tests and fuzz tests in Elm. The
very fact that we are listing two tests hints at the
usefulness of fuzz tests.
If we wanted to make sure that Expect.equal will
return zero for any number subtracted by itself, we
could possibly write a number of tests to confirm
this behavior.
However, writing a fuzz test would be a much
better solution. Let’s look at the updated code:
module Example exposing (..)

-- import Fuzz exposing (Fuzzer, int, list, string)

import Expect exposing (Expectation)


import Test exposing (..)

suite : Test
suite =
describe "Zero is equal to zero"
[ test "Zero is equal to one minus one" <|
\_ -> Expect.equal 0 (1 - 1)
, test "Zero is equal to two minus two" <|
\_ -> Expect.equal 0 (2 - 2)
]

Now, let’s rewrite this unit test in the form of a fuzz


test.
We’ll uncomment the Fuzz import, and we’ll
randomize some of the inputs in our test, as
follows:
module Example exposing (..)

import Expect exposing (Expectation)


import Fuzz exposing (Fuzzer, int, list, string)
import Test exposing (..)

suite : Test
suite =
describe "Zero is equal to zero"
[ fuzz int "Zero is equal to random number minus itse
\randomNumber -> Expect.equal 0 (randomNumber - r
]

This test will run 100 times. This makes us draw


conclusions about our passing tests with a lot more
confidence than we would writing just plain old
unit tests.
Looking at the anatomy of our fuzz test syntax, we
can see that the fuzz function takes three
parameters:

a fuzzer

a String to describe the fuzz test

an anonymous function (which takes an


actual argument, contrary to a unit test,
which takes a Unit as its parameter)

Thus, this fuzz test’s fuzzer is used to generate int


values. The int fuzzer generates random Ints. The
String used to describe the fuzz test is
straightforward. We are using the String that reads
Zero is equal to random number minus itself.
Finally, our anonymous function \ takes the
parameter we named randomNumber. Since we are using
randomNumber in our actual Expect.equal assertion, it

makes perfect sense to pass this argument to the


anonymous function.
Working with more than
one fuzzer
Similarly to how we can use map2, map3, map4, and so
on, depending on how many fields we are decoding
from JSON values to Elm values, we also have at
our disposal the following functions: fuzz2, fuzz3,
fuzz4, and fuzz5.

Let’s update our Example.elm test so that it uses the


fuzz2 function. To make things simple, we’ll test
commutation in number addition. This is the rule
in mathematics that says that if we flip the order of
numbers that we are adding, the result will be the
same:
module Example exposing (..)

import Expect exposing (Expectation)


import Fuzz exposing (Fuzzer, int, list, string)
import Test exposing (..)

suite : Test
suite =
describe "Zero is equal to zero"
[ fuzz2 int int "Swiching positions on two numbers bein
\randomNumber1 randomNumber2 ->
Expect.equal (randomNumber1 + randomNumber2) (ran
]

All 100 tests pass as expected. It is important to


note that the console output will list these 100 tests
as only one test passing, since in effect that is what
is happening. There is indeed only one test being
run, albeit with 100 different combinations of
values.
Summary
In this chapter, we learned the basics of unit testing
and fuzz testing in Elm. We learned about the
structure and syntax of tests in Elm:

We learned about describe, Expect.equals, and


test

We discussed how and why to use \_ and <|


in Elm tests

We wrote tests to decode JSON strings to


Elm values

We looked at the difference between unit


tests and fuzz tests

We wrote fuzz tests that take more than one


argument

However, the material covered in this chapter is


only the beginning. TDD (test-driven development)
is an approach to building software that embraces
testing throughout our application’s life cycle;
actually, as its name suggests, it is an approach in
which testing drives the complete development
process. With the topics covered in this course, you
should be able to start implementing tests routinely
in your Elm development.
In the next chapter, we will look at authentication
in our Elm apps.
Integrating Elm with
Rails
Welcome to Chapter 10, Integrating Elm with
Rails. The goal of this chapter is to implement
simple user authentication with Elm on the
frontend. For the backend, we will use Ruby on
Rails 5.1.5.
The topics we will cover include:

Setting up a basic Rails 5.1.5 app using


Codeanywhere

Installing Ruby 2.5.0 and Rails 5.1.5 on


Codeanywhere

Creating a brand new Rails project

Integrating Elm with Rails 5.1.5

Adding our Elm weather app to our Rails


app

After completing this chapter, you will be able to:

Install a brand new Rails project on


Codeanywhere

Utilize the webpacker gem to work with Elm


more easily
Integrate Elm-powered modules inside an
existing Rails application
Setting up a basic Rails
5.1.5 app using
Codeanywhere
Codeanywhere is an online Integrated
Development Environment (IDE), with a
number of great features. You can access it by
pointing your browser to codeanywhere.com.
To begin using it, a registration is needed.
Before actually being able to use a containerized
Ubuntu 14.04 environment to run our Rails 5.1.5 app,
Codeanywhere requires new registrants to confirm
their email.

To begin, choose a new Blank Development Stack


on Ubuntu 14.04, give it a name, for example,
elmrails, and click the CREATE button.
Clicking the CREATE button will make a new
Ubuntu 14.04 container, ready for us to work on.
A very important thing to note is the message we
get in the editor pane of the Codeanywhere app.
The editor pane is nothing fancy; it is simply the
largest pane in an open Codeanywhere app, which
lists open files as tabs. The message that we get
upon a successful container creation lists the name
of the container and some additional information,
as follows:

elmrails Container
Development Stack with your custom installed tools

This Codeanywhere Container comes with:

2GB of Disc Storage


256MB RAM (+ 512 MB swap)
Sudo access
SSH access on hostXX.codeanyhost.com:12345
Access to all HTTP and Websocket ports
The operating system running on this Container is Ubuntu 14.0

To access an application running on your Container use the fo

http://<containername>-<username><123456>.codeanyapp.com

To access your application over HTTPS, make sure your applica

https://<containername>-<username><123456>.codeanyapp.com

If the port is blocked by your firewall you can connect throu

http://port-XX.<containername>-<username><123456>.codeanyapp.

Of all the information listed here, this is the most


important to note:

To access an application running on your Container use the fo

http://<containername>-<username><123456>.codeanyapp.com

The reason why this information is important is


simple: that address will be the address that you’ll
need to open in your browser to see your working
Rails application. However, it is still too early to do
that, since we still need to install Ruby 2.5.0 and
Rails 5.1.5.
Installing Ruby 2.5.0
and Rails 5.1.5 on
Codeanywhere
Once you have completed the container setup, click
the link to the Editor, and you can get started
installing Ruby 2.5.0 and Rails 5.1.5. Detailed
instructions on how to do this can be found in the
excellent online guide at the gorails.com website, at
the following address: https://gorails.com/setup/ubuntu/1
4.04.

Here, we’ll just list all the commands that need to


be run in the console, as a quick overview. Note
that the following commands can also be found in
the code files that accompany this chapter.
Note that, at a certain point, you will need to run
the command rbenv install 2.5.0, which will make the
console seem frozen. To have a better idea of what
is happening, you can run this command with the
following flag:
rbenv install --verbose 2.5.0

Running this flag with the rbenv install command


will turn on verbose logging so that you can see the
progress of the installation in more detail, and not
wonder if the app is doing anything at all.
The complete list of commands that should be run
is as follows:
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo
sudo apt-get update
sudo apt-get install git-core curl zlib1g-dev build-essential
cd
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
exec $SHELL
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/pl
echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"
exec $SHELL
rbenv install 2.5.0
ruby -v
rbenv global 2.5.0
ruby -v
gem install bundler
node -v
gem install rails -v 5.1.5
rbenv rehash
rails -v

Now that we have installed all the prerequisites,


we’ll install a Rails app.
Creating a brand new
Rails project
To install a new Rails app, your bash needs to be
inside the workspace folder. To verify you are in the
right location, you should see the following output
in the console:

cabox@box-codeanywhere:~/workspace$

Now, simply run the following command:


rails new simple --webpack=elm

Note that the word simple can be anything that you


want. The word you choose to use will determine
the folder name inside of which your Rails 5 app
will be installed.
Next, in the left-hand pane of the Codeanywhere
app, right-click on the bottom-most entry, which
reads elmrails, and in the contextual menu that pops
up, click the ‘refresh’ command.
Doing this will result in the tree structure being
refreshed, and now you’ll be able to see another
folder, titled simple, listed inside the elmrails project
folder, like this:

∨ elmrails
> simple

Clicking on the simple folder’s title inside the left-


hand panel will toggle the visibility of the folder
structure. If you click it to twirl it open, you’ll see
the following structure:
∨ simple
> .git
> app
> bin
> config
> db
> elm-stuff
> lib
> log
> node_modules
> public
> test
> tmp
> vendor
.babelrc
.gitignore
.postcssrc.yml
config.ru
elm-package.json
Gemfile
Gemfile.lock
package.json
Rakefile
README.md
yarn.lock

Now that you are sure that you have successfully


installed Rails, it’s time to take it for a spin. Point
your bash to the simple folder by running the
following command:
cd simple

Now, type the command to spin up a Rails server:

rails s

You should get the following output in the console:


=> Booting Puma
=> Rails 5.1.5 application starting in development
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.11.3 (ruby 2.5.0-p0), codename: Love Song
* Min threads: 5, max threads: 5

* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop

Now’s the time to look at our boilerplate Rails app


running online. To do that, we’ll need to reference
the information we got after our Ubuntu container
was created, that is, we need to check for the
following information:

To access an application running on your Container use the fo

http://<containername>-<username><123456>.codeanyapp.com

Note that when we ran the rails s command, the


console output ended with this line:
* Listening on tcp://0.0.0.0:3000

That means that our Rails app is being served at


the address that satisfies the following pattern:
http://<containername>-<username><123456>.codeanyapp.com:3000

If you open a new tab in your browser and point it


at this address, you’ll see the Rails 5 splash screen
greeting:
<Rails logo>
Yay! You’re on Rails!
<A drawing>
Rails version: 5.1.5
Ruby version: 2.5.0 (x86_64-linux)
In this section, we have successfully installed Rails.
Not only that, but by providing the --webpack=elm flag
to our Rails project creation command, we
successfully integrated Elm with our new Rails
project.
That’s why we had to go through a relatively long
installation of Rails 5.1.5 at the beginning of this
chapter: to have a simple, painless integration of
Rails and Elm.
In the next section, we’ll start modifying our Rails
app and start using Elm inside of it.
Integrating Elm with
Rails 5.1.5
When running a rails new <projectname> command
without passing any additional flags, the resultant
Rails app that gets created is a default one, without
Elm integration. It might be a good exercise to
delete the folder of the new Rails project we made
in the previous step and run a new Rails project
creation command, this time without passing flags.
However, to avoid the waste of time involved in
doing such a thing, we are going to list here some
of the additional files that get created when we pass
the --webpack=elm flag to the rails new <projectname>
command.
To understand how Elm integrates with Rails, the
best place to start is the app/javascript folder, and
inside that folder, the hello_elm.js file.
Here are the contents of hello_elm.js:

// Run this example by adding <%= javascript_pack_tag "hello_


// head of your layout file, like app/views/layouts/applicati
// It will render "Hello Elm!" within the page.

import Elm from '../Main'

document.addEventListener('DOMContentLoaded', () => {
const target = document.createElement('div')

document.body.appendChild(target)
Elm.Main.embed(target)
})

This JavaScript file will take the view rendered by


the Main.elm file and embed it inside a div element.
But where will this div element live? That is entirely
up to us.
For example, the first three lines of hello_elm.js are
actually one-line JavaScript comments, and they
give us exact instructions on how to render the
Elm-powered view in the layout file of our Rails
site.
As the commented-out instruction reads, we need
to go to the default layout view of our Rails app,
which is application.html.erb.
The full path to this file is
. So let’s open
simple/app/views/layouts/application.html.erb
this file and add javascript_pack_tag to its head, like this:
<!DOCTYPE html>
<html>
<head>
<title>Simple</title>
<%= csrf_meta_tags %>

<%= stylesheet_link_tag 'application', media: 'all', 'dat


<%= javascript_include_tag 'application', 'data-turbolink
<%= javascript_pack_tag "hello_elm" %>
</head>
...

Finally, to hook everything up, we need to


make application#index the default route that our Rails
app will open. We will do that by updating the
routes.rb file, located inside the simple/config/ folder:

Rails.application.routes.draw do
# For details on the DSL available within this file, see ht
root 'application#index'
end

In this file, we only added the root 'application#index'


route, just above the last line of the file. Doing this
says to our Rails app that we want to route it to the
application controller’s index action as the default
route, or root route.
If we refresh our running app in the browser, at
this point we will get the following error:
Unknown action
The action 'index' could not be found for ApplicationControll

That means that we need to add the index view for


our ApplicationController. To do that, let’s simply
create a new folder inside the views folder. We’ll call
this new folder application. Next, inside the application
folder, let’s create a new file, which we’ll call
index.html.erb.

Note that in order to create folders and files in


Codeanywhere, you need to right-click on the parent
folder that should contain them. Then, choose either the
Create Folder or the Create File command, depending
on your needs. Once you make your choice, a modal
will appear, and you will be able to input the name of
your file or folder, depending on the command you
picked.

Next, open the newly-created index.html.erb file and


type only one line of code inside it:
<h1>Successfully integrated Elm and Rails!</h1>
<div></div>

Next, save everything and refresh the page in which


you are viewing your Rails app (which had the
Rails splash screen).
Next, open the bash tab that has your Rails server
running. Notice that the console is outputting the
following messages:
[Webpacker] Compiling…
[Webpacker] Compiled all packs in /home/cabox/workspace/simpl
Completed 200 OK in 187478ms (Views: 187460.0ms)
These messages mean that the webpacker is at
work, compiling your Elm code and integrating it
inside your Rails project. As you can see from the
output, it took about 188 seconds for this
compilation to complete. Once it’s done, you can
refresh the page in which you are previewing your
served Rails app, and now, instead of the default
Rails app splash screen, you should see the
following message in the upper-center part of the
screen:
Hello Elm!

Having your Rails app serve this message in the


default route means that you have successfully
integrated Elm with Rails.
Now that we have successfully integrated Elm with
our Rails app, let’s further improve on our results
by building something a bit more complex.
Adding our Elm
weather app to our
Rails app
In this section, we will improve our Rails app by
adding the Elm weather app to it. We will use the
finished version of the weather app, as we had it at
the end of Chapter 8, Adding More Features to the
Weather App.
To do that, let’s open the elm-package.json file in the
root of our Rails app, and update it with additional
dependencies, so that the complete updated file
looks as follows:
{
"version": "1.0.0",
"summary": "helpful summary of your project, less than 80
"repository": "https://github.com/user/project.git",
"license": "BSD3",
"source-directories": [
"app/javascript"
],
"exposed-modules": [],
"dependencies": {
"debois/elm-mdl": "8.1.0 <= v < 9.0.0",
"elm-lang/core": "5.0.0 <= v < 6.0.0",
"elm-lang/html": "2.0.0 <= v < 3.0.0",
"elm-lang/http": "1.0.0 <= v < 2.0.0",
"myrho/elm-round": "1.0.2 <= v < 2.0.0"
},
"elm-version": "0.18.0 <= v < 0.19.0"
}

Next, we need to install the packages. We can’t


install them directly via Elm, but rather by using
Yarn. The command we need to run is this:

yarn run elm package install


The console will print out the following to the
screen:
yarn run v1.5.1
$ /home/cabox/workspace/simple/node_modules/.bin/elm package
Some new packages are needed. Here is the upgrade plan.

Install:
debois/elm-dom 1.2.3
debois/elm-mdl 8.1.0
elm-lang/dom 1.1.1
elm-lang/mouse 1.0.1
elm-lang/window 1.0.1
myrho/elm-round 1.0.2

Do you approve of this plan? [Y/n] y


Starting downloads...

● debois/elm-dom 1.2.3
● elm-lang/mouse 1.0.1
● elm-lang/dom 1.1.1
● debois/elm-mdl 8.1.0
● elm-lang/window 1.0.1
● myrho/elm-round 1.0.2

Packages configured successfully!


Done in 25.23s.

Now, we need to make a new file, which we will call


WeatherApp.elm. Next, we can simply copy and paste
the complete weather app into this file. The only
change we need to make to it is in the first line,
which needs to look like this:
module WeatherApp exposing (..)

The complete code for the WeatherApp.elm file can be


found in the code files for Chapter 8, Adding More
Features to the Weather App, (since we are using
the code that can be found in the Main.elm file of the
improved weather app we completed at the end of C
hapter 8, Adding More Features to the Weather
App).
Next, we also need to add the JS file that will be the
entry point for our WeatherApp.elm file. We’ll name this
JS file similar to hello_elm.js, meaning we’ll separate
the words using the underscore character, and we
will not capitalize any letters in the filename. So,
create a new file inside the javascript/packs folder and
call it weather_app.js.
Next, add the following code to this new file:
import Elm from './WeatherApp'

document.addEventListener('DOMContentLoaded', () => {
const target = document.getElementById('weather-app')

Elm.WeatherApp.embed(target);
})

Next, we need to update our


views/application/index.html.erb file by adding the

javascript_pack_tag and passing it a div with the id


attribute of weather-app:

<h1>
Successfully integrated Elm and Rails!
</h1>
<%= javascript_pack_tag "weather_app" %>
<div id="weather-app"></div>
<div></div>

Now, we can restart our Rails server:

rails s

Note that, this time, webpacker is going to take


quite some time to compile all the newly-added
packages (Http, Material, and Round). You should see
this message in the console after running the rails s
command:
rails s
=> Booting Puma
=> Rails 5.1.5 application starting in development
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.11.3 (ruby 2.5.0-p0), codename: Love Song
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop
Started GET "/" for ......... at 2018-03-10 04:04:30 -0500
Cannot render console from .........! Allowed networks: 127.0
Processing by ApplicationController#index as HTML
Rendering application/index.html.erb within layouts/applica
[Webpacker] Compiling…

Finally, after the compilation is complete, we will


see this message logged to the console:

[Webpacker] Compiled all packs in /home/cabox/workspace/simpl


Rendered application/index.html.erb within layouts/applicat
Completed 200 OK in 97585ms (Views: 96676.2ms)

If we open our Rails app in the browser and refresh


it, we will see the following web page:
As you can see, our Rails-powered website
currently has three parts: a static h1 heading and
two separate Elm-powered apps, the weather app
and the hello app.
This means that we have successfully added two
separate Elm apps to our Rails app. This way, we
can slowly start introducing Elm widgets to an
existing Rails-powered website. This is great if we
work in a team of developers who do not know Elm
but want to see how it integrates with an existing
project and learn from it. Another possible case
where this approach would be useful is if the
management in our company decides to give Elm a
try without going all in.
Summary
In this chapter, we covered the following topics:

Setting up a basic Rails 5.1.5 app using


Codeanywhere

Installing Ruby 2.5.0 and Rails 5.1.5 on


Codeanywhere

Creating a brand new Rails project

Integrating Elm with Rails 5.1.5

Adding our Elm weather app to our Rails


app

Armed with this knowledge, we can start adding


standalone Elm-powered modules to our Rails
apps.
This brings us to the end of this book. However,
your journey with Elm is just beginning. Here are
some useful resources if you are interested in
learning more about this excellent language (the
following links are listed in no particular order):

https://github.com/bryanjenningz/25-elm-examples

https://www.smashingmagazine.com/2018/01/learning-elm-

drum-sequencer-part-1/

http://elmprogramming.com/

https://www.reddit.com/r/elm/
http://elmlang.herokuapp.com/

https://www.elmlog.com/meetup_groups

https://discourse.elm-lang.org/
Visit elmcasts.com
And finally, a brand new project started by the
author of this book. A website dedicated to learning
Elm.
In the tradition of sites such as railscasts.com and lara
casts.com, I’ve decided to name it elmcasts.com. The

website should be up and running by the time this


book is on the market.
Other Books You May
Enjoy
If you enjoyed this book, you may be interested in
these other books by Packt:

Microservices with Clojure


Anuj Kumar
ISBN: 978-1-78862-224-0

Explore the pros and cons of monolithic


and microservice architectures

Use Clojure to effectively build a reallife


application using Microservices

Gain practical knowledge of the Clojure


Pedestal framework and how to use it to
build Microservices

Explore various persistence patterns and


learn how to use Apache Kafka to build
event-driven microservice architectures

Secure your Microservices using JWT


Monitor Microservices at scale using the
ELK stack

Deploy Microservices at scale using


container orchestration platforms such as
Kubernetes

Mastering JavaScript Functional


Programming
Federico Kereki
ISBN: 978-1-78728-744-0

Create more reliable code with closures and


immutable data

Convert existing methods into pure


functions, and loops into recursive methods

Develop more powerful applications with


currying and function composition

Separate the logic of your system from


implementation details

Implement composition and chaining


techniques to simplify coding
Use functional programming techniques
where it makes the most sense
Leave a review - let
other readers know
what you think
Please share your thoughts on this book with
others by leaving a review on the site that you
bought it from. If you purchased the book from
Amazon, please leave us an honest review on this
book’s Amazon page. This is vital so that other
potential readers can see and use your unbiased
opinion to make purchasing decisions, we can
understand what our customers think about our
products, and our authors can see your feedback on
the title that they have worked with Packt to create.
It will only take a few minutes of your time, but is
valuable to other potential customers, our authors,
and Packt. Thank you!

You might also like