We can do better
@Jack_Franklin, bit.ly/elm-polyconf 15
Slide 16
Slide 16 text
Explicit, component
based approach
@Jack_Franklin, bit.ly/elm-polyconf 16
Slide 17
Slide 17 text
@Jack_Franklin, bit.ly/elm-polyconf 17
Slide 18
Slide 18 text
Components in some form
• Angular 2
• Ember
• Vue.js
• Cycle.js
• React
@Jack_Franklin, bit.ly/elm-polyconf 18
Slide 19
Slide 19 text
Who actually likes
JavaScript these days?!
@Jack_Franklin, bit.ly/elm-polyconf 19
Slide 20
Slide 20 text
Explicit about state
@Jack_Franklin, bit.ly/elm-polyconf 20
Slide 21
Slide 21 text
Single source of truth
@Jack_Franklin, bit.ly/elm-polyconf 21
Slide 22
Slide 22 text
Views represent state
view(state) => HTML
@Jack_Franklin, bit.ly/elm-polyconf 22
Slide 23
Slide 23 text
View functions are pure
view(state1) => HTML1
view(state1) => HTML1
view(state2) => HTML2
@Jack_Franklin, bit.ly/elm-polyconf 23
Slide 24
Slide 24 text
Explicitly define all actions that
can modify state
function addTodo() {
return {
type: 'USER_ADD_TODO',
text: 'Buy Milk'
}
}
@Jack_Franklin, bit.ly/elm-polyconf 24
Slide 25
Slide 25 text
Have update functions that can
handle actions
update(action, state) => newState
@Jack_Franklin, bit.ly/elm-polyconf 25
Slide 26
Slide 26 text
update encapsulates most
business logic
@Jack_Franklin, bit.ly/elm-polyconf 26
Slide 27
Slide 27 text
Unidirectional Data Flow
user clicks
-> action
-> update(action, state)
-> view(newState)
@Jack_Franklin, bit.ly/elm-polyconf 27
Slide 28
Slide 28 text
Recommended Reading
➡ Unidirectional User Interface Architectures by
André Staltz
@Jack_Franklin, bit.ly/elm-polyconf 28
Slide 29
Slide 29 text
@Jack_Franklin, bit.ly/elm-polyconf 29
Slide 30
Slide 30 text
Explicit vs Magic
@Jack_Franklin, bit.ly/elm-polyconf 30
Slide 31
Slide 31 text
Magic!
function setNewUser(name) {
$scope.user = { name : 'jack' };
}
@Jack_Franklin, bit.ly/elm-polyconf 31
Even more explicit!
type Msg =
NewUser String
| LogOut
@Jack_Franklin, bit.ly/elm-polyconf 33
Slide 34
Slide 34 text
Even more explicit because the
compiler says so
update msg model =
case msg of
NewUser name ->
{ model | user = name }
This `case` does not have branches for all possibilities.
22|> case msg of
You need to account for the following values:
LogOut
Add a branch to cover this pattern!
@Jack_Franklin, bit.ly/elm-polyconf 34
Slide 35
Slide 35 text
@Jack_Franklin, bit.ly/elm-polyconf 35
Slide 36
Slide 36 text
Elm: a language to solve
these problems.
@Jack_Franklin, bit.ly/elm-polyconf 36
Slide 37
Slide 37 text
Elm, my Dear Watson
-- Sherlock Holmes
@Jack_Franklin, bit.ly/elm-polyconf 37
Slide 38
Slide 38 text
Not the finished article
@Jack_Franklin, bit.ly/elm-polyconf 38
Slide 39
Slide 39 text
Not the perfect language
(yet?!)
@Jack_Franklin, bit.ly/elm-polyconf 39
Slide 40
Slide 40 text
Not the perfect solution
to all our problems
@Jack_Franklin, bit.ly/elm-polyconf 40
Slide 41
Slide 41 text
No runtime errors!
@Jack_Franklin, bit.ly/elm-polyconf 41
Types
add : Int -> Int -> Int
isEven : Int -> Bool
@Jack_Franklin, bit.ly/elm-polyconf 50
Slide 51
Slide 51 text
Union Types
type Filter
= ShowAll
| ShowCompleted
| ShowActive
showTodos : Filter -> List Todo -> List Todo
showTodos filter todos =
case filter of
ShowAll -> todos
ShowCompleted -> List.filter (\t -> t.complete) todos
ShowActive -> List.filter (\t -> not t.complete) todos
@Jack_Franklin, bit.ly/elm-polyconf 51
Slide 52
Slide 52 text
Union Types
! They can be checked by the compiler (typos
are spotted)
! Compiler ensures all are dealt with in
case ... of
! Easy to change / add a new one: add it and fix
each compiler error!
@Jack_Franklin, bit.ly/elm-polyconf 52
Slide 53
Slide 53 text
Type aliases
type alias Person =
{ name : String
, age : Int
}
incrementAge : Person -> Person
incrementAge person =
{ person | person.age = person.age + 1 }
@Jack_Franklin, bit.ly/elm-polyconf 53
Slide 54
Slide 54 text
! Clearer code, typed in your domain specific
objects.
! Compiler can guarantee you're meeting the
type requirements.
! No more 'undefined is not a function' !
@Jack_Franklin, bit.ly/elm-polyconf 54
Slide 55
Slide 55 text
type alias Person =
{ name : String
, age : Int
}
@Jack_Franklin, bit.ly/elm-polyconf 55
Slide 56
Slide 56 text
Robust
@Jack_Franklin, bit.ly/elm-polyconf 56
Slide 57
Slide 57 text
Immutability brings guarantees
var person = { name: 'Jack', age: 24 };
incrementAge(person);
// has this mutated?
// does it return a new person?
// #javascript
@Jack_Franklin, bit.ly/elm-polyconf 57
Slide 58
Slide 58 text
Sweet, sweet Elm
let
person = { name = "Jack", age = 24 }
in
incrementAge person
! person is untouched
! incrementAge has to return a new person
! goodbye mutation bugs
@Jack_Franklin, bit.ly/elm-polyconf 58
Slide 59
Slide 59 text
Modules
! Everything is scoped
! Modules explicitly declare what they expose
! Modules explicitly declare what they import
@Jack_Franklin, bit.ly/elm-polyconf 59
Slide 60
Slide 60 text
Dealing with nothing
No more null.
@Jack_Franklin, bit.ly/elm-polyconf 60
Slide 61
Slide 61 text
Maybe
type Maybe a =
Just a
| Nothing
It's either Just some value, or Nothing.
@Jack_Franklin, bit.ly/elm-polyconf 61
Slide 62
Slide 62 text
Maybe
type alias Model = {
user : Maybe User
}
view : Model -> Html Msg
view model =
case model.user of
Nothing ->
div [] [ text "No user!" ]
Just user ->
div [] [ text ("Logged in as " ++ user.name) ]
@Jack_Franklin, bit.ly/elm-polyconf 62
Slide 63
Slide 63 text
You must handle all cases
of missing / pending data
@Jack_Franklin, bit.ly/elm-polyconf 63
Slide 64
Slide 64 text
Task
A module for async actions that might fail
(HTTP).
Task errType successType
Task String User
- if it fails, fail with a String
- if it succeeds, succeed with a User
@Jack_Franklin, bit.ly/elm-polyconf 64
Slide 65
Slide 65 text
You have to deal with errors.
Task doesn't let you not.
(We'll come back to this later).
@Jack_Franklin, bit.ly/elm-polyconf 65
Slide 66
Slide 66 text
Commands and Subcriptions
• Cmd : an async thing that Elm should run for
you
• Sub : a subscription to some data you care
about that might change
(We'll come back to these).
@Jack_Franklin, bit.ly/elm-polyconf 66
Slide 67
Slide 67 text
Adjustment time
This does take time to get used to
• Syntax
• Types
• Immutablility
• Compiling!
• Maybe and explicit error handling
@Jack_Franklin, bit.ly/elm-polyconf 67
Slide 68
Slide 68 text
When you apply a pattern everywhere it ends up
becoming a guarantee: that 100% of your code
will follow that pattern. Once you have that
guarantee you can build powerful developer tools
or cross module features.
-- Everywhereness as a Foundation, André Staltz
@Jack_Franklin, bit.ly/elm-polyconf 68
Slide 69
Slide 69 text
The Elm compiler make sure that 100% of your
code is thoroughly checked against corner cases
and error cases. This everywhereness becomes a
guarantee. And it is only because of this guarantee
that Elm programs have virtually no runtime errors.
-- Everywhereness as a Foundation, André Staltz
@Jack_Franklin, bit.ly/elm-polyconf 69
Slide 70
Slide 70 text
The Elm Architecture
@Jack_Franklin, bit.ly/elm-polyconf 70
Slide 71
Slide 71 text
The three parts:
model : Model
view : Model -> Html Msg
update : Msg -> Model -> Model
@Jack_Franklin, bit.ly/elm-polyconf 71
Slide 72
Slide 72 text
@Jack_Franklin, bit.ly/elm-polyconf 72
Slide 73
Slide 73 text
Counter
@Jack_Franklin, bit.ly/elm-polyconf 73
Slide 74
Slide 74 text
First, define your Model
type alias Model = Int
initialModel : Model
initialModel = 0
@Jack_Franklin, bit.ly/elm-polyconf 74
Slide 75
Slide 75 text
Secondly, define your Msgs
type Msg = Increment | Decrement
@Jack_Franklin, bit.ly/elm-polyconf 75
Slide 76
Slide 76 text
Thirdly, define your update:
update : Msg -> Model -> Model
update msg model =
case msg of
Increment -> model + 1
Decrement -> model - 1
@Jack_Franklin, bit.ly/elm-polyconf 76
Slide 77
Slide 77 text
Fourthly, define your view:
view : Model -> Html Msg
view model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] [ text (toString model) ]
, button [ onClick Increment ] [ text "+" ]
]
@Jack_Franklin, bit.ly/elm-polyconf 77
Slide 78
Slide 78 text
Finally, hook it all up!
main =
Html.App.beginnerProgram
{ model = initialModel
, view = view
, update = update
}
@Jack_Franklin, bit.ly/elm-polyconf 78
Slide 79
Slide 79 text
@Jack_Franklin, bit.ly/elm-polyconf 79
Slide 80
Slide 80 text
! We left the view until last.
! Explained all our logic before the UI.
! Notice how easy update would be to test.
@Jack_Franklin, bit.ly/elm-polyconf 80
Slide 81
Slide 81 text
Side Effects
@Jack_Franklin, bit.ly/elm-polyconf 81
Slide 82
Slide 82 text
Explicitly model side effects.
Hand off to Elm, it will hand back later.
@Jack_Franklin, bit.ly/elm-polyconf 82
Slide 83
Slide 83 text
Commands
@Jack_Franklin, bit.ly/elm-polyconf 83
Slide 84
Slide 84 text
Whenever you need to perform some
background work, you have to give Elm a
command.
Elm will go off, perform the command, and call
your update function once it's done.
@Jack_Franklin, bit.ly/elm-polyconf 84
Slide 85
Slide 85 text
model : Model
view : Model -> Html Msg
update : Msg -> Model -> (Model, Cmd Msg)
@Jack_Franklin, bit.ly/elm-polyconf 85
Firstly, define the model:
type alias GithubPerson =
{ name : String
, company : String
}
type alias Model =
{ username : String
, githubPerson : Maybe GithubPerson
}
@Jack_Franklin, bit.ly/elm-polyconf 88
Slide 89
Slide 89 text
Secondly, define your Msgs
type Msg
= NewGithubData GithubPerson
| FetchGithubData
| FetchError Http.Error
@Jack_Franklin, bit.ly/elm-polyconf 89
Slide 90
Slide 90 text
Thirdly, define your update (and note the new
type)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
FetchError error ->
-- deal with error here in reality
NewGithubData person ->
( { model | githubPerson = Just person }, Cmd.none )
FetchGithubData ->
( model, fetchGithubData model.username )
@Jack_Franklin, bit.ly/elm-polyconf 90
Slide 91
Slide 91 text
NewGithubData person ->
( { model | githubPerson = Just person }, Cmd.none )
-- Cmd.none === do nothing
FetchGithubData ->
( model, fetchGithubData model.username )
--- fetchGithubData returns a command
--- which Elm will run for us
@Jack_Franklin, bit.ly/elm-polyconf 91
Slide 92
Slide 92 text
@Jack_Franklin, bit.ly/elm-polyconf 92
Slide 93
Slide 93 text
Fourthly, define your view:
view : Model -> Html Msg
view model =
case model.githubPerson of
Just person ->
div []
[ text (person.name ++ ", " ++ person.company) ]
Nothing ->
div [] [
button [ onClick FetchGithubData ] [ text "Load!" ]
]
@Jack_Franklin, bit.ly/elm-polyconf 93
Finally, hook it all together!
main =
Html.App.program
{ init = init
, view = view
, update = update
, subscriptions = \_ -> Sub.none
}
@Jack_Franklin, bit.ly/elm-polyconf 95
Slide 96
Slide 96 text
@Jack_Franklin, bit.ly/elm-polyconf 96
Slide 97
Slide 97 text
@Jack_Franklin, bit.ly/elm-polyconf 97
Slide 98
Slide 98 text
@Jack_Franklin, bit.ly/elm-polyconf 98
Slide 99
Slide 99 text
@Jack_Franklin, bit.ly/elm-polyconf 99
Slide 100
Slide 100 text
@Jack_Franklin, bit.ly/elm-polyconf 100
Slide 101
Slide 101 text
@Jack_Franklin, bit.ly/elm-polyconf 101
Slide 102
Slide 102 text
@Jack_Franklin, bit.ly/elm-polyconf 102
Slide 103
Slide 103 text
That feels like a lot of code / effort!
-- All of you.
@Jack_Franklin, bit.ly/elm-polyconf 103
Slide 104
Slide 104 text
Boilerplate vs
Explicitness
@Jack_Franklin, bit.ly/elm-polyconf 104
Slide 105
Slide 105 text
Benefits increase as
application grows
@Jack_Franklin, bit.ly/elm-polyconf 105
Slide 106
Slide 106 text
Fetching Data
• Decoding JSON from an API into an Elm
record.
• Use Elm's HTTP library to make the request.
• Code in the GitHub repo!
• Come and grab me if you'd like to see it in
person.
@Jack_Franklin, bit.ly/elm-polyconf 106
Scaling your application
@Jack_Franklin, bit.ly/elm-polyconf 108
Slide 109
Slide 109 text
@Jack_Franklin, bit.ly/elm-polyconf 109
Slide 110
Slide 110 text
The Elm Ecosystem
@Jack_Franklin, bit.ly/elm-polyconf 110
Slide 111
Slide 111 text
elm reactor
Easily run a project in the browser with no
tooling required.
@Jack_Franklin, bit.ly/elm-polyconf 111
Slide 112
Slide 112 text
@Jack_Franklin, bit.ly/elm-polyconf 112
Slide 113
Slide 113 text
elm package
@Jack_Franklin, bit.ly/elm-polyconf 113
Slide 114
Slide 114 text
@Jack_Franklin, bit.ly/elm-polyconf 114
Slide 115
Slide 115 text
Semantic versioning ensured.
~/git/elm-statey > elm package diff
Comparing jackfranklin/elm-statey 2.0.0 to local changes...
This is a MAJOR change.
------ Changes to module Statey - MAJOR ------
Changed:
- makeState : String -> Statey.State
+ makeState : Statey.State
@Jack_Franklin, bit.ly/elm-polyconf 115
Slide 116
Slide 116 text
elm format
@Jack_Franklin, bit.ly/elm-polyconf 116
Slide 117
Slide 117 text
There's so much more I
haven't covered.
@Jack_Franklin, bit.ly/elm-polyconf 117
Slide 118
Slide 118 text
So, why / when
should you use
Elm?
@Jack_Franklin, bit.ly/elm-polyconf 118
Slide 119
Slide 119 text
You're fed up of
undefined
function errors
that take up loads of
time
@Jack_Franklin, bit.ly/elm-polyconf 119
Slide 120
Slide 120 text
You're fed up of
packages on npm
breaking semantic
versioning
@Jack_Franklin, bit.ly/elm-polyconf 120
Slide 121
Slide 121 text
You want to develop
with the confidence
of Types and a
clever compiler to
back you up
@Jack_Franklin, bit.ly/elm-polyconf 121
Slide 122
Slide 122 text
You're happy to
"ride the wave" and
deal with a
language still
growing and settling
down
@Jack_Franklin, bit.ly/elm-polyconf 122
Slide 123
Slide 123 text
You're happy to
build more
packages than
depend on existing
solutions which may
not exist in Elm
@Jack_Franklin, bit.ly/elm-polyconf 123
Slide 124
Slide 124 text
But what if this
talk has put me
off Elm?
@Jack_Franklin, bit.ly/elm-polyconf 124
Slide 125
Slide 125 text
Elm does take time to
learn, so please don't give
up after 30 minutes of
slides!
guide.elm-lang.org
@Jack_Franklin, bit.ly/elm-polyconf 125
Slide 126
Slide 126 text
Elm the language brings
many concepts that are
language agnostic
@Jack_Franklin, bit.ly/elm-polyconf 126
Slide 127
Slide 127 text
The Elm Architecture
@Jack_Franklin, bit.ly/elm-polyconf 127
Slide 128
Slide 128 text
Explicitness across your
application
@Jack_Franklin, bit.ly/elm-polyconf 128
Defining your application step
by step
1. Define your model.
2. Define your actions.
3. Define your update function.
4. Define your view.
5. Repeat.
@Jack_Franklin, bit.ly/elm-polyconf 131
Slide 132
Slide 132 text
Will everyone be writing
Elm in 1/2/5 years?
@Jack_Franklin, bit.ly/elm-polyconf 132