Introduction to Elm

James Morcom (@morcs, morcs.com)

What is Elm?

  • Programming Language (created by Evan Czaplicki)
    • Browser apps
    • Pure functional language
    • Syntax like Haskell, F# (ML family)
    • Compiles to JavaScript
  • Front-end framework (like React/Angular/Vue)
    • Single state/event dispatch model (Redux)
    • REPL, package manager, hot-reloading etc.

Why Elm?

Imagine a world...

  • No null/undefined
  • No language cruft
  • No exceptions
  • Illegal states unrepresentable
  • Compiler finds the errors...

Just functions and data!

Also...

  • Learn FP
  • It's fast!!

https://bit.ly/2HqPNqX

"Pure" functions?

  • All functions in Elm are "pure" and "total"
  • Can be represented by a lookup table
Input (Int) Output (Int)
-1 1
0 0
1 1
2 4
3 9
... ...
  • Every possible input appears once
  • Every input has an output
  • Output always the same

"Pure" functions?

Input (Int) Output (Int)
-1 1
0 0
1 1
... ...
  • Can't read from globals, fields (this), DBs, console (no hidden inputs, deterministic)
  • Can't write to globals, DBs, send HTTP either!
    (no hidden outputs, no side-effects)
  • Easy testing (no mocking required)
  • Easy to optimize/parallellise

Syntax - Functions & values

 

circumference r = 
    2 * pi * r
add x y = x + y
pi = 3.142
result = add 3 5
-- result == 7

Syntax - Lists

 

results =
  [ circumference 2
  , add 3 4
  , 12
  ]
numbers = [1, 2, 3, 4, 5]

Syntax - Markup

  • Angular uses separate HTML templates
  • React uses JSX - one file, two languages
  • In Elm, markup is just code

Elm is different

Exercise 0

Warmup

https://morcs.com/introduction-to-elm/

Ctrl-click "View Exercise"

Try compiling - should get an error

Warmup

Elm code

HTML output

main = table [] []
-- hit compile, compiler validates HTML!
<table></table>
main = table [ class "table" ] []
            
-- again, hit compile to validate HTML
<table class="table"></table>
main = 
    table [ class "table" ] [ 
        tr [] [] 
    ]
    
-- try the FORMAT button!
<table class="table">
    <tr></tr>            
</table>
main =
    table [ class "table" ]
        [ tr [] 
            [ th [] [] 
            ]
            
-- hit compile, valid HTML!
        ]
<table class="table">
    <tr> <th></th> </tr>            
</table>
main =
    table [ class "table" ]
        [ tr []
            [ th [ scope "row" ] [ text "Elm" ] ]
        ]
        
-- should start to see output
<table class="table">
    <tr> <th scope="row">Elm</th> </tr>            
</table>
main =
    table [ class "table" ]
        [ tr []
            [ th [ scope "row" ] [ text "Elm" ]
            , td [] [ text "10" ]
            ]
        ]
        
<table class="table">
  <tr> <th scope="row">Elm</th> <td>10</td> </tr>
</table>
main =
    table [ class "table" ]
        [ renderRow
        ]


renderRow =
    tr []
        [ th [ scope "row" ] [ text "Elm" ]
        , td [] [ text "10" ]
        ]
<table class="table">
  <tr> <th scope="row">Elm</th> <td>10</td> </tr>
</table>
main =
    table [ class "table" ]
        [ renderRow "Elm"
        , renderRow "React"
        ]

renderRow : String -> Html msg -- type annotation
renderRow name =
    tr []
        [ th [ scope "row" ] [ text name ]
        , td [] [ text "10" ]
        ]
<table class="table">
  <tr> <th scope="row">Elm</th> <td>10</td> </tr>
  <tr> <th scope="row">React</th> <td>10</td> </tr>
</table>
main =
    table [ class "table" ]
        [ renderRow "Elm" 10 -- web component!
        , renderRow "React" 9
        ]

renderRow : String -> Int -> Html msg
renderRow name score =
    tr []
        [ th [ scope "row" ] [ text name ]
        , td [] [ text (toString score) ]
        ]
<table class="table">
  <tr> <th scope="row">Elm</th> <td>10</td> </tr>
  <tr> <th scope="row">React</th> <td>9</td> </tr>
</table>

Exercise 1

Markup

https://morcs.com/introduction-to-elm/

Instructions/markup are on this page!

Types

james : { name : String, age : Int }

james = 
  { name = "James"
  , age = 12 
  }
james : (String, Int)

james = ("James", 12)

Records

Tuples

Dealing with states

Use record type?

model : 
  { cards : List Card
  , loaded : Bool
  , errorMessage : String
  , hasError : Bool
  }
model =
  { cards = []
  , loaded = True
  , errorMessage = "Cards didn't load"
  , hasError = True
  }

Union types

type Model 
  = Loading
  | Success (List Card)
  | Error String
case model of
  Loading ->
    -- show a loading spinner
  Success cards ->
    -- render all the cards
  Error msg ->
    -- show msg with red styling
model = Error "Badness happened"
type Bool 
    = True 
    | False

type List a
    = NonEmptyList a (List a)
    | Empty

  • Presence/absence of value (no need for null)
  • Success/failure state
    (no need for exceptions)

Exercise 2

Union types

https://morcs.com/introduction-to-elm/

Interactivity

The essense of every Elm program:

  • Model — a nice representation of your application state.
  • View — a function that turns models into HTML.
  • Update — given a user-input messages and the model, produce a new model.

The back-end isn't ready yet

 

...but the client wants to see what it'll look like in various states

 

Lets add some buttons so they can switch between states.

model : model

view : model -> Html msg

update : msg -> model -> model
type Model
    = Loading
    | Success (List Card)
    | Error String
type Msg
    = TestSuccess
    | TestError String

Buttons

Exercise 3

The update function

https://morcs.com/introduction-to-elm/

type Maybe a 
    = Nothing 
    | Just a

No nulls!

Only Maybes

 

var empty = new List<Card>();
var card = empty.FirstOrDefault();
var card = empty.First();
var name = card.Name

Exception!!!

Exception!!!

List.head : List a -> Maybe a

C#

Elm

card = List.head []
name = card.name

Won't compile!!!

name = case List.head [] of
         Nothing ->
           "Anonymous"
         Just card ->
           card.name
Input (List Int) Output (Int) ?
[] BOOM! / null
[1] 1
[1,2] 1
[2] 2
... ...

C#/Linq

First / FirstOrDefault

Input (List Int) Output (Maybe Int)
[] Nothing
[1] Just 1
[1,2] Just 1
[2] Just 2
... ...

Elm

List.head

Exercise 4

The Maybe type

https://morcs.com/introduction-to-elm/

type Result error value
    = Ok value
    | Err error

No exceptions!

Only Results

Extending messages

init : (model, Cmd msg)

update : msg -> model -> (model, Cmd msg)

view : model -> Html msg

subscriptions : model -> Sub msg

Exercise 5

Calling an API

https://morcs.com/introduction-to-elm/

More Exercises

 

https://morcs.com/introduction-to-elm/

How can Elm do stuff?

  • Describe events/effects as data that can be inputs/outputs of  pure functions
  • Inputs come from framework
  • Outputs describe effects for framework to carry out

...if we only have pure functions

Input Output
Card X was clicked Model with Card X selected
Load more was clicked Go fetch URL (with callback)

Syntax - More Lists

 

result =
    List.map circumference <| List.filter isEven numbers
result =
    List.map circumference numbers
result =
    numbers
        |> List.filter isEven
        |> List.map circumference
result =
    List.map circumference (List.filter isEven numbers)

Syntax - Maybe vs List

 

type Maybe a 
    = Nothing 
    | Just a
type List a
    = Empty
    | NonEmptyList a (List a)

Syntax - Maybe vs List

 

cardName : Maybe Card -> String
cardName selected =
    selected
        |> Maybe.map .name
        |> Maybe.withDefault "-- not selected --"

selected = Just { name = "Brian" }
result = cardName selected
-- result == "Brian"

selected = Nothing
result = cardName selected
-- result == "-- not selected --" 

Multiple Parameters

add x y = x + y
addThree = add 3
result = addThree 4
circumference = \r -> 2 * pi * r
add = \x -> (\y -> x + y)
addThree = add 3 -- \y -> 3 + y

Lambda syntax

add      : number -> number -> number
addThree : number -> number
result   : number

Multiple Parameters

Introduction to Elm

By James Morcom

Introduction to Elm

  • 1,172