Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PolyConf: Elm for JS Developers

PolyConf: Elm for JS Developers

A talk on Elm and why it's cool.

Jack Franklin

June 30, 2016
Tweet

More Decks by Jack Franklin

Other Decks in Technology

Transcript

  1. @Jack_Franklin, bit.ly/elm-polyconf 1

    View Slide

  2. !
    @Jack_Franklin, bit.ly/elm-polyconf 2

    View Slide

  3. @Jack_Franklin, bit.ly/elm-polyconf 3

    View Slide

  4. @Jack_Franklin
    @Jack_Franklin, bit.ly/elm-polyconf 4

    View Slide

  5. @Jack_Franklin, bit.ly/elm-polyconf 5

    View Slide

  6. @Jack_Franklin, bit.ly/elm-polyconf 6

    View Slide

  7. @Jack_Franklin, bit.ly/elm-polyconf 7

    View Slide

  8. The great tooling problem
    @Jack_Franklin, bit.ly/elm-polyconf 8

    View Slide

  9. We must accept that
    complex applications are
    hard to build
    @Jack_Franklin, bit.ly/elm-polyconf 9

    View Slide

  10. Trends in Complex
    JavaScript Applications
    @Jack_Franklin, bit.ly/elm-polyconf 10

    View Slide

  11. Two Way Data Binding
    @Jack_Franklin, bit.ly/elm-polyconf 11

    View Slide

  12. Object.observe
    @Jack_Franklin, bit.ly/elm-polyconf 12

    View Slide

  13. MVC / MVVC /
    MCVCVMMCVCCC
    @Jack_Franklin, bit.ly/elm-polyconf 13

    View Slide

  14. "Let's replicate Rails"
    @Jack_Franklin, bit.ly/elm-polyconf 14

    View Slide

  15. We can do better
    @Jack_Franklin, bit.ly/elm-polyconf 15

    View Slide

  16. Explicit, component
    based approach
    @Jack_Franklin, bit.ly/elm-polyconf 16

    View Slide

  17. @Jack_Franklin, bit.ly/elm-polyconf 17

    View Slide

  18. Components in some form
    • Angular 2
    • Ember
    • Vue.js
    • Cycle.js
    • React
    @Jack_Franklin, bit.ly/elm-polyconf 18

    View Slide

  19. Who actually likes
    JavaScript these days?!
    @Jack_Franklin, bit.ly/elm-polyconf 19

    View Slide

  20. Explicit about state
    @Jack_Franklin, bit.ly/elm-polyconf 20

    View Slide

  21. Single source of truth
    @Jack_Franklin, bit.ly/elm-polyconf 21

    View Slide

  22. Views represent state
    view(state) => HTML
    @Jack_Franklin, bit.ly/elm-polyconf 22

    View Slide

  23. View functions are pure
    view(state1) => HTML1
    view(state1) => HTML1
    view(state2) => HTML2
    @Jack_Franklin, bit.ly/elm-polyconf 23

    View Slide

  24. 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

    View Slide

  25. Have update functions that can
    handle actions
    update(action, state) => newState
    @Jack_Franklin, bit.ly/elm-polyconf 25

    View Slide

  26. update encapsulates most
    business logic
    @Jack_Franklin, bit.ly/elm-polyconf 26

    View Slide

  27. Unidirectional Data Flow
    user clicks
    -> action
    -> update(action, state)
    -> view(newState)
    @Jack_Franklin, bit.ly/elm-polyconf 27

    View Slide

  28. Recommended Reading
    ➡ Unidirectional User Interface Architectures by
    André Staltz
    @Jack_Franklin, bit.ly/elm-polyconf 28

    View Slide

  29. @Jack_Franklin, bit.ly/elm-polyconf 29

    View Slide

  30. Explicit vs Magic
    @Jack_Franklin, bit.ly/elm-polyconf 30

    View Slide

  31. Magic!
    function setNewUser(name) {
    $scope.user = { name : 'jack' };
    }
    @Jack_Franklin, bit.ly/elm-polyconf 31

    View Slide

  32. Explicit!
    function update(action, state) {
    switch (action.type) {
    case 'NEW_USER':
    return Object.assign({}, state, {
    user: { name: action.name }
    });
    }
    }
    @Jack_Franklin, bit.ly/elm-polyconf 32

    View Slide

  33. Even more explicit!
    type Msg =
    NewUser String
    | LogOut
    @Jack_Franklin, bit.ly/elm-polyconf 33

    View Slide

  34. 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

    View Slide

  35. @Jack_Franklin, bit.ly/elm-polyconf 35

    View Slide

  36. Elm: a language to solve
    these problems.
    @Jack_Franklin, bit.ly/elm-polyconf 36

    View Slide

  37. Elm, my Dear Watson
    -- Sherlock Holmes
    @Jack_Franklin, bit.ly/elm-polyconf 37

    View Slide

  38. Not the finished article
    @Jack_Franklin, bit.ly/elm-polyconf 38

    View Slide

  39. Not the perfect language
    (yet?!)
    @Jack_Franklin, bit.ly/elm-polyconf 39

    View Slide

  40. Not the perfect solution
    to all our problems
    @Jack_Franklin, bit.ly/elm-polyconf 40

    View Slide

  41. No runtime errors!
    @Jack_Franklin, bit.ly/elm-polyconf 41

    View Slide

  42. • Functional
    • Typed
    • Compiled
    @Jack_Franklin, bit.ly/elm-polyconf 42

    View Slide

  43. • Expressive, clear code
    • Self documenting
    • Robust
    @Jack_Franklin, bit.ly/elm-polyconf 43

    View Slide

  44. Learning curve
    ahead!
    @Jack_Franklin, bit.ly/elm-polyconf 44

    View Slide

  45. Expressive, clear code
    @Jack_Franklin, bit.ly/elm-polyconf 45

    View Slide

  46. Functional Programming
    add(1, 2)
    (add 1 2)
    List.map (\x -> x + 2) [1, 2, 3, 4]
    List.map ((+) 2) [1, 2, 3, 4]
    @Jack_Franklin, bit.ly/elm-polyconf 46

    View Slide

  47. Pipes
    incrementWeight (incrementHeight (incrementAge (makePerson "jack")))
    makePerson "jack"
    |> incrementAge
    |> incrementHeight
    |> incrementWeight
    @Jack_Franklin, bit.ly/elm-polyconf 47

    View Slide

  48. Clean syntax
    incrementAge person =
    { person | age = person.age + 1 }
    add x y =
    x + y
    addTwo =
    add 2
    @Jack_Franklin, bit.ly/elm-polyconf 48

    View Slide

  49. Self documenting
    @Jack_Franklin, bit.ly/elm-polyconf 49

    View Slide

  50. Types
    add : Int -> Int -> Int
    isEven : Int -> Bool
    @Jack_Franklin, bit.ly/elm-polyconf 50

    View Slide

  51. 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

    View Slide

  52. 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

    View Slide

  53. 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

    View Slide

  54. ! 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

    View Slide

  55. type alias Person =
    { name : String
    , age : Int
    }
    @Jack_Franklin, bit.ly/elm-polyconf 55

    View Slide

  56. Robust
    @Jack_Franklin, bit.ly/elm-polyconf 56

    View Slide

  57. 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

    View Slide

  58. 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

    View Slide

  59. Modules
    ! Everything is scoped
    ! Modules explicitly declare what they expose
    ! Modules explicitly declare what they import
    @Jack_Franklin, bit.ly/elm-polyconf 59

    View Slide

  60. Dealing with nothing
    No more null.
    @Jack_Franklin, bit.ly/elm-polyconf 60

    View Slide

  61. Maybe
    type Maybe a =
    Just a
    | Nothing
    It's either Just some value, or Nothing.
    @Jack_Franklin, bit.ly/elm-polyconf 61

    View Slide

  62. 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

    View Slide

  63. You must handle all cases
    of missing / pending data
    @Jack_Franklin, bit.ly/elm-polyconf 63

    View Slide

  64. 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

    View Slide

  65. 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

    View Slide

  66. 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

    View Slide

  67. 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

    View Slide

  68. 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

    View Slide

  69. 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

    View Slide

  70. The Elm Architecture
    @Jack_Franklin, bit.ly/elm-polyconf 70

    View Slide

  71. The three parts:
    model : Model
    view : Model -> Html Msg
    update : Msg -> Model -> Model
    @Jack_Franklin, bit.ly/elm-polyconf 71

    View Slide

  72. @Jack_Franklin, bit.ly/elm-polyconf 72

    View Slide

  73. Counter
    @Jack_Franklin, bit.ly/elm-polyconf 73

    View Slide

  74. First, define your Model
    type alias Model = Int
    initialModel : Model
    initialModel = 0
    @Jack_Franklin, bit.ly/elm-polyconf 74

    View Slide

  75. Secondly, define your Msgs
    type Msg = Increment | Decrement
    @Jack_Franklin, bit.ly/elm-polyconf 75

    View Slide

  76. 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

    View Slide

  77. 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

    View Slide

  78. Finally, hook it all up!
    main =
    Html.App.beginnerProgram
    { model = initialModel
    , view = view
    , update = update
    }
    @Jack_Franklin, bit.ly/elm-polyconf 78

    View Slide

  79. @Jack_Franklin, bit.ly/elm-polyconf 79

    View Slide

  80. ! 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

    View Slide

  81. Side Effects
    @Jack_Franklin, bit.ly/elm-polyconf 81

    View Slide

  82. Explicitly model side effects.
    Hand off to Elm, it will hand back later.
    @Jack_Franklin, bit.ly/elm-polyconf 82

    View Slide

  83. Commands
    @Jack_Franklin, bit.ly/elm-polyconf 83

    View Slide

  84. 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

    View Slide

  85. model : Model
    view : Model -> Html Msg
    update : Msg -> Model -> (Model, Cmd Msg)
    @Jack_Franklin, bit.ly/elm-polyconf 85

    View Slide

  86. @Jack_Franklin, bit.ly/elm-polyconf 86

    View Slide

  87. Fetching someone's
    GitHub data.
    @Jack_Franklin, bit.ly/elm-polyconf 87

    View Slide

  88. 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

    View Slide

  89. Secondly, define your Msgs
    type Msg
    = NewGithubData GithubPerson
    | FetchGithubData
    | FetchError Http.Error
    @Jack_Franklin, bit.ly/elm-polyconf 89

    View Slide

  90. 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

    View Slide

  91. 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

    View Slide

  92. @Jack_Franklin, bit.ly/elm-polyconf 92

    View Slide

  93. 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

    View Slide

  94. Fifthly (new step), define your init:
    initialModel : Model
    initialModel =
    { username = "jackfranklin"
    , githubPerson = Nothing
    }
    init : ( Model, Cmd Msg )
    init =
    ( initialModel, Cmd.none )
    @Jack_Franklin, bit.ly/elm-polyconf 94

    View Slide

  95. 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

    View Slide

  96. @Jack_Franklin, bit.ly/elm-polyconf 96

    View Slide

  97. @Jack_Franklin, bit.ly/elm-polyconf 97

    View Slide

  98. @Jack_Franklin, bit.ly/elm-polyconf 98

    View Slide

  99. @Jack_Franklin, bit.ly/elm-polyconf 99

    View Slide

  100. @Jack_Franklin, bit.ly/elm-polyconf 100

    View Slide

  101. @Jack_Franklin, bit.ly/elm-polyconf 101

    View Slide

  102. @Jack_Franklin, bit.ly/elm-polyconf 102

    View Slide

  103. That feels like a lot of code / effort!
    -- All of you.
    @Jack_Franklin, bit.ly/elm-polyconf 103

    View Slide

  104. Boilerplate vs
    Explicitness
    @Jack_Franklin, bit.ly/elm-polyconf 104

    View Slide

  105. Benefits increase as
    application grows
    @Jack_Franklin, bit.ly/elm-polyconf 105

    View Slide

  106. 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

    View Slide

  107. http://bit.ly/elm-polyconf
    @Jack_Franklin, bit.ly/elm-polyconf 107

    View Slide

  108. Scaling your application
    @Jack_Franklin, bit.ly/elm-polyconf 108

    View Slide

  109. @Jack_Franklin, bit.ly/elm-polyconf 109

    View Slide

  110. The Elm Ecosystem
    @Jack_Franklin, bit.ly/elm-polyconf 110

    View Slide

  111. elm reactor
    Easily run a project in the browser with no
    tooling required.
    @Jack_Franklin, bit.ly/elm-polyconf 111

    View Slide

  112. @Jack_Franklin, bit.ly/elm-polyconf 112

    View Slide

  113. elm package
    @Jack_Franklin, bit.ly/elm-polyconf 113

    View Slide

  114. @Jack_Franklin, bit.ly/elm-polyconf 114

    View Slide

  115. 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

    View Slide

  116. elm format
    @Jack_Franklin, bit.ly/elm-polyconf 116

    View Slide

  117. There's so much more I
    haven't covered.
    @Jack_Franklin, bit.ly/elm-polyconf 117

    View Slide

  118. So, why / when
    should you use
    Elm?
    @Jack_Franklin, bit.ly/elm-polyconf 118

    View Slide

  119. You're fed up of
    undefined
    function errors
    that take up loads of
    time
    @Jack_Franklin, bit.ly/elm-polyconf 119

    View Slide

  120. You're fed up of
    packages on npm
    breaking semantic
    versioning
    @Jack_Franklin, bit.ly/elm-polyconf 120

    View Slide

  121. You want to develop
    with the confidence
    of Types and a
    clever compiler to
    back you up
    @Jack_Franklin, bit.ly/elm-polyconf 121

    View Slide

  122. You're happy to
    "ride the wave" and
    deal with a
    language still
    growing and settling
    down
    @Jack_Franklin, bit.ly/elm-polyconf 122

    View Slide

  123. 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

    View Slide

  124. But what if this
    talk has put me
    off Elm?
    @Jack_Franklin, bit.ly/elm-polyconf 124

    View Slide

  125. 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

    View Slide

  126. Elm the language brings
    many concepts that are
    language agnostic
    @Jack_Franklin, bit.ly/elm-polyconf 126

    View Slide

  127. The Elm Architecture
    @Jack_Franklin, bit.ly/elm-polyconf 127

    View Slide

  128. Explicitness across your
    application
    @Jack_Franklin, bit.ly/elm-polyconf 128

    View Slide

  129. Types
    @Jack_Franklin, bit.ly/elm-polyconf 129

    View Slide

  130. Immutability / Functional
    Programming
    @Jack_Franklin, bit.ly/elm-polyconf 130

    View Slide

  131. 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

    View Slide

  132. Will everyone be writing
    Elm in 1/2/5 years?
    @Jack_Franklin, bit.ly/elm-polyconf 132

    View Slide

  133. @Jack_Franklin, bit.ly/elm-polyconf 133

    View Slide

  134. • guide.elm-lang.org
    • elm-lang.org/docs
    • elm-lang.org/community
    • bit.ly/elm-polyconf
    • speakerdeck.com/jackfranklin
    @Jack_Franklin, bit.ly/elm-polyconf 134

    View Slide

  135. @Jack_Franklin
    javascriptplayground.com
    @Jack_Franklin, bit.ly/elm-polyconf 135

    View Slide

  136. @Jack_Franklin, bit.ly/elm-polyconf 136

    View Slide