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