Elm Accelerated

The Accelerated series embraces new technologies to allow you to learn with interactive learning Apps and other resources like books, quizzes and more. They aim to overcome the 'Google Effect' (I can Google it so my brain doesn't even try to retain it) and achieve genuine fluency with Modern Software Development technologies.

Apps

The primary version of Elm Accelerated is the App. Currently the App is out on iOS.

But on this site you can also find:

Quiz

Let's rate your Elm knowledge. Once you have used the Elm Accelerated App for a while you should be able to answer all of these (and similar) questions with relative ease.

Sample Chapters

Below are a few chapters from the book.

Introduction

It is an almost impossible task to write a great book on programming. It requires not just skill and effort on the part of the author, but a rare alignment of experience, aptitude and interest with the reader. A dry reference is possible but of limited value. A tutorial unlikely to be pitched at the right level. This book tries something different in its goal of teaching the core of the Elm programming language and ecosystem.

It is not primarily a book. It is (available as) a Flash Card App and has associated resources, quizzes, audio content and more online.

It focuses on the most useful details. You absolutely should work through real examples, but these should come from the demands of your own project or failing that in a structured, challenge based way (I recommend sources for these).

It targets fluency not superficial knowledge, but includes the hard parts and tools to make learning them reasonable.

I want to make it possible for you to learn Elm more quickly and more thoroughly than is otherwise possible.

Audience

You should already know at least one mainstream programming language. But you may not be familiar with functional programming or have studied computer science formally. If you have never programmed before this is a terrible place to start. If you are familiar with functional programming, particularly ML languages, you can probably skip or skim the more conceptual parts.

Elm

Often programmers will talk of trade offs between languages. That one 'Serious' language is hard, verbose but performant and that other little language is easy, quick, dirty but slow. (I have examples in mind, but sharing them seems ill advised!) Don't believe them. It is 2018 and you should demand all the things. Languages like Elm, Kotlin and Swift reject these kinds of false choices and give us concise, clear code, performance, safety and clarity. They are simply better than many common alternatives.

Elm is the friendliest language I've ever encountered and has great tools today. It combines theory with pragmatism, looks beautiful and is remarkably useful in the right context. As long as you want to create applications in a web browser it is likely to be a great choice, particularly if it is somewhat complex with many different interactions or updates. But even if you don't end up using it in production, just learning will give you a much better feel for modern Javascript approaches to state like Redux.

How to use this book

This book consists of lots of concise chapters. A small number target core ideas or examples. Most focus on a topic and give a concise but clear description. Ideally you would mainly use the Flash Card App to drive learning from this book, looking up things you are struggling with. On the Elm Accelerated website you will find more resources like quizzes or audio summaries. These provide additional tools to accelerate your learning.

Install Elm

Before continuing you should probably install Elm (if you haven't already). Go to elm-lang.org and follow the instructions there for your operating system. You will also want to get the Elm plugins for your text editor of choice (Visual Studio Code support is great, but similar tools exist for Atom and Sublime). It is also worth getting the elm-format tool and configuring your editor to format your code on save.

Try Elm

Once you've done that why not try out a few simple commands in the elm-repl. Don't worry if you don't know exactly what is going on (this book will teach you), but notice how you can evaluate simple expressions and create functions with very little noise. Notice also how unusually friendly and helpful any error messages are. This is one of the many areas where Elm is way ahead of the curve.

 > elm-repl

 > 1 + 1
 2 : number
 > 4 > 5
 False : Bool
 > if True then "hi" else "bye"
 "hi" : String
 > f x = x * x
 <function> : number -> number
 > f 4
 16 : number
 > f "hi"
 -- TYPE MISMATCH ------------------------
 The argument to function `f` is causing a 
 mismatch.
 3|   f "hi"
        ^^^^
 Function `f` is expecting the argument to
 be:
     number
 But it is:
     String

Lists

We create lists in Elm with square brackets. All items must be of the same type.

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

> ["a", "b", "c"]
["a","b","c"] : List String

A list actually consists of a first element (head) and rest of list (tail). We use :: to 'glue' these together.

> 1 :: []
[1] : List number

> 1 :: [2]
[1,2] : List number

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

In most functional programming languages lists are the most common data structure. We can naturally write recursive algorithms which operate on the head and tail. For example:

> f x = case x of
         h :: t -> h + (f t)
         [] -> 0

<function> : List number -> number

> f [1,2,3]
6 : number

However, in Elm we will more commonly use standard list functions. There are a few basic functions. The easy ones are isEmpty and length which do the obvious things.

isEmpty : List a -> Bool
length : List a -> Int

We can also reverse a list or find out if a particular item is in a list.

reverse : List a -> List a
member : a -> List a -> Bool

We can extract the head or tail (a list can be thought of as head :: tail) with

head : List a -> Maybe a
tail : List a -> Maybe (List a)

Notice how these are Maybe a as an empty list has neither head nor tail. However a one item list does have a tail, the empty list!

The simplest ways to extract items from a list are:

take : Int -> List a -> List a
drop : Int -> List a -> List a
filter : (a -> Bool) -> List a -> List a

take takes up to the number of items supplied from a list. drop drops the supplied number of items (where possible). Where filter returns a list with only items which return True from the supplied function.

> List.take 2 [1,2,3]
[1,2] : List number

> List.take 4 [1,2,3]
[1,2,3] : List number

> List.drop 2 [1,2,3]
[3] : List number

> List.filter (\n -> n > 1) [1,2,3]
[2,3] : List number

A very common list function (for example in generating labels for a data visualisation or indicies for some kind of processing is range). It generates a list of integers between and including the two values supplied. For example.

> List.range 1 5
[1,2,3,4,5] : List Int

> List.range 1 10
[1,2,3,4,5,6,7,8,9,10] : List Int

But if that range is empty an empty list is returned.

> List.range 1 -4
[] : List Int

We can compose lists with append and concat. Append joins two lists together. Whereas concat takes a list of lists and appends them all together into one list.

> List.append [1,2] [3,4]
[1,2,3,4] : List number

> List.concat [[1,2], [3,4], [5,6]]
[1,2,3,4,5,6] : List number

To do simple processing of a list we use map which applies a function to each element.

map : (a -> b) -> List a -> List b

> List.map (\n -> n * n) [1,2,3]
[1,4,9] : List number

> List.map String.toUpper ["hi", "world"]
["HI","WORLD"] : List String

There are actually versions of map for functions operating on multiple lists. For example map2.

> List.map2 (*) [1,2,3] [4,5,6] 
[4,10,18] : List number

Here we wrap the infix operator in (*) to pass as a regular function.

There are some standard functions which work on entire lists.

sum : List number -> number
product : List number -> number
maximum : List comparable -> Maybe comparable
minimum : List comparable -> Maybe comparable

These all do the obvious thing (though it is not obvious that these functions should be in the List module). There are also two functions for looking at logical queries on a list all and any: each takes a function that maps each element to True or False. Then if all (any) hold true for all (any) it returns True, otherwise False.

all : (a -> Bool) -> List a -> Bool
any : (a -> Bool) -> List a -> Bool

We can do custom operations on entire lists with foldl, foldr and scanl. foldl is commonly called reduce in other languages. These take a function which takes a list item, the accumulated value and returns a new accumulated value; an initial accumulated value; and a list. The function then returns the final accumulated value having 'folded' across the entire list. That sounds really complex but let's look at some simple examples.

> List.foldl (+) 0 [1,2,3,4]
10 : number

> List.foldl (*) 1 [2,3,4]
24 : number

foldr just does the same but 'folds' from the right (i.e. starts from the end of the list). scanl instead of returning the final accumulated value, returns all the values as we pass through the list.

> List.foldr (+) 0 [1,2,3,4]
10 : number
> List.scanl (*) 1 [2,3,4]
[1,2,6,24] : List number

There are several advanced maps. We look at two here: filterMap and indexedMap. The first applies a function to a list which returns a Maybe. It then drops any Nothings (perhaps resulting in an empty list). This can make data processing pipelines a lot more concise.

filterMap : (a -> Maybe b) -> List a -> List b

queries 
    |> filterMap getResultIfAny
    |> map convertResult 

indexedMap allows you to write functions to apply to list elements along with their index (for example when drawing a data visualisation we may want to offset each item by their index (place in list) scaled).

indexedMap : (Int -> a -> b) -> List a -> List b

indexedMap (\idx d -> { x = (toFloat idx) * dx
                      , y = y * dy 
                      }
           )
           data

We have three standard ways to sort a list. The first works in the 'standard' way with standard Elm primitives which are comparable (numbers, characters, strings, lists of comparables and tuples of comparables).

sort : List comparable -> List comparable

> List.sort [2,4,1,2,6,9]
[1,2,2,4,6,9] : List number

We can also sort by something extracted from each list item with sortBy. If these items are records we can use .key to pull out the key item from each.

sortBy : (a -> comparable) -> List a -> List a

> sortBy .key listOfRecordsWithKey

If we want full control of sort we do this with sortWith and the helper Order type. Here we write a function that takes two list elements and then returns an Order, either: LT, EQ and GT (less than, equal, greater than). This is similar to javascript where you might pass a sort function which returns a negative number, zero or a positive number to indicate how two elements are related.

sortWith : (a -> a -> Order) -> List a -> List a

sortFrom is not a core List sorting function.

There are a handful of other List functions. Two of note are partition which generates two lists, one which satisfies a condition, the other which fails and unzip which takes a list of a Tuple and converts it into a Tuple of lists of each item.

partition : (a -> Bool) -> List a -> (List a, List a)
unzip : List (a, b) -> (List a, List b)

Regular Expressions

For more elaborate String validations and matching we can use regular expressions. Elm provides a nice set of functions for working with regular expressions in its core library.

We construct regular expressions (type Regex) with regex which takes a string. Notice the double \\ this is so that a backslash is included, rather than the next letter being escaped.

> regex "0123"
> regex "Elm"
> regex "\\d{1,3}"

There are four basic functions to work with Regex: contains, find, replace and split.

contains does what you might expect

contains : Regex -> String -> Bool

> contains (regex "\\d") "1234"
True
> contains (regex "\\D") "1234"
False
> contains (regex "[a-c]") "1234"
False
> contains (regex "[a-c]") "12a23"
True

Before we look at the other main functions we need to learn about the data structures Elm provides to make working with them pleasant. To specify the number of (potential) matches we have the elegant and precise type:

type HowMany
    = All
    | AtMost Int

And when we get matches the type used is:

type alias Match = 
    { match : String
    , submatches : List (Maybe String)
    , index : Int
    , number : Int
    }

Where the match is the match, submatches and parenthetical capture groups (i.e. parts of match grouped in brackets), the index is original location and the number is the index of the match itself.

Actually once you understand these types there is very little left to worry about (unlike other languages where you often seem to have to look up weird details of what regular expression methods return).

find : HowMany 
    -> Regex 
    -> String 
    -> List Match

findThreeNumbers = find (AtMost 3) (regex "\\d")

As find returns a list of Match you can pull out the matches or submatches or perform further processing very straightforwardly.

Let's consider replace. Again the function signature tells you pretty much all you need to know.

replace:  HowMany 
        -> Regex 
        -> (Match -> String) 
        -> String 
        -> String

The only non obvious part is the Match -> String i.e. you can actually write a function from the match to the replacement. This gives you access to all the Match fields, so you can do whatever you want.

In many cases this function might produce the same output. To signal to the Elm compiler this is intentional use an _.

removeUpToTwoNumbers = replace 
                        (AtMost 2) 
                        (regex "\\d") 
                        (\_ -> "")

shortenWords = replace 
                All 
                (regex "\\w+") 
                (\{match} -> String.left 3 match)

The final function is split

split : HowMany 
        -> Regex 
        -> String 
        -> List String

> split All (regex ",") "1,2,3"
["1","2","3"]

You might want to put these back together again (after some processing or filtering). For that use

join : String -> List String -> String

Resources

Elm Accelerated website

From the Introduction chapter of Elm Accelerated

elm-lang.org

From the Introduction chapter of Elm Accelerated

Full comment documentation

From the Comments chapter of Elm Accelerated

elm-lang.org

From the How to learn Elm chapter of Elm Accelerated

Video course on Elm

From the How to learn Elm chapter of Elm Accelerated

A few free video lessons

From the How to learn Elm chapter of Elm Accelerated

Exercism

From the How to learn Elm chapter of Elm Accelerated

Slideshow example in Elm

From the How to learn Elm chapter of Elm Accelerated

A simple Elm game

From the How to learn Elm chapter of Elm Accelerated

Medium Clone (in Elm)

From the How to learn Elm chapter of Elm Accelerated

Example Elm Game

From the The Elm Architecture Part 2 chapter of Elm Accelerated

react-elm-components

From the Using Elm within React (and Webpack) chapter of Elm Accelerated

Elm Webpack Loader

From the Using Elm within React (and Webpack) chapter of Elm Accelerated

json-to-elm

From the JSON Decoding chapter of Elm Accelerated