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

FullStackFest: Elm for JS Developers

Jack Franklin
September 08, 2016

FullStackFest: Elm for JS Developers

Jack Franklin

September 08, 2016
Tweet

More Decks by Jack Franklin

Other Decks in Technology

Transcript

  1. View Slide

  2. !

    View Slide

  3. View Slide

  4. View Slide

  5. View Slide

  6. It's a sign!

    View Slide

  7. @Jack_Franklin

    View Slide

  8. View Slide

  9. View Slide

  10. View Slide

  11. "Let's replicate rails"

    View Slide

  12. MVC / MVVC /
    MCVCVMMCVCCC

    View Slide

  13. Two way data binding
    and dirty checking

    View Slide

  14. Object.observe

    View Slide

  15. We can do better

    View Slide

  16. View Slide

  17. View Slide

  18. Changes in data

    View Slide

  19. The application owns its
    state

    View Slide

  20. Single source of truth

    View Slide

  21. http://todo.com/filter/completed
    {
    todos: [{ ... }, { ... }],
    filter: 'completed'
    }
    Duplicate knowledge = out of sync quickly!

    View Slide

  22. Most state lives in one
    place, with exceptions.

    View Slide

  23. It Depends

    View Slide

  24. Our UI becomes a
    representation of state
    at a given time

    View Slide

  25. {
    todos: [{
    text: 'Do an Elm talk',
    done: true
    },
    ...
    ]
    }

    View Slide

  26. {
    todos: [{
    text: 'Do an Elm talk',
    done: false
    },
    ...
    ]
    }

    View Slide

  27. {
    todos: [{
    text: 'Do an Elm talk',
    done: true
    },
    ...
    ]
    }

    View Slide

  28. Efficient rendering is not
    a developer concern1
    1 within reason, not always, be reasonable, thanks!

    View Slide

  29. Explicitly define how the
    user can modify the state

    View Slide

  30. And be able to trace it

    View Slide

  31. angular.controller('MyCtrl', function($scope) {
    $scope.onClick = function() {
    $scope.x = $scope.x + 1;
    }
    function fetchDataAndIncrement() {
    fetch('api.com/data').then(function(data) {
    $scope.data = data;
    $scope.x = $scope.x + 1;
    });
    }
    });

    View Slide

  32. function addTodo() {
    return {
    type: 'USER_ADD_TODO',
    text: 'Buy Milk'
    }
    }
    These can be logged, stored, tracked, serialized and
    so on.

    View Slide

  33. Have update functions that can handle actions and
    update the state
    update(action, state) => newState

    View Slide

  34. const action = { type: 'USER_LOG_OUT'; };
    const currentState = { user: { name: 'Jack' } };
    const newState = update(action, currentState);
    newState => { user: undefined };

    View Slide

  35. update encapsulates most
    business logic

    View Slide

  36. Your logic and your UI are
    separated

    View Slide

  37. Data flow

    View Slide

  38. View Slide

  39. Recommended Reading
    ➡ Unidirectional User Interface Architectures by
    André Staltz

    View Slide

  40. View Slide

  41. Explictly deal with all user interactions and state
    changes
    function update(action, state) {
    switch (action.type) {
    case 'NEW_USER':
    return Object.assign({}, state, {
    user: { name: action.name }
    });
    }
    }

    View Slide

  42. Even better!
    type Msg =
    NewUser String
    | LogOut

    View Slide

  43. View Slide

  44. Elm: a language to solve
    these problems.

    View Slide

  45. Not the perfect language
    (yet?!)

    View Slide

  46. Not the perfect solution
    to all our problems

    View Slide

  47. → Functional
    → Typed
    → Compiled

    View Slide

  48. → Expressive, clear code
    → Self documenting
    → Robust

    View Slide

  49. Learning curve ahead!

    View Slide

  50. Expressive, clear code
    (add 1 2)
    List.map (\x -> x + 2) [1, 2, 3, 4]
    List.map ((+) 2) [1, 2, 3, 4]

    View Slide

  51. Pipes
    incrementWeight (incrementHeight (incrementAge (makePerson "jack")))
    makePerson "jack"
    |> incrementAge
    |> incrementHeight
    |> incrementWeight

    View Slide

  52. !
    makePerson "jack"
    |> incrementAge
    |> incrementHeight
    |> incrementWeight
    |> incrementWeight
    |> incrementWeight
    |> incrementWeight
    |> incrementWeight
    |> incrementWeight
    |> incrementWeight
    |> incrementWeight
    |> incrementWeight
    |> incrementWeight
    |> incrementWeight

    View Slide

  53. incrementAge person =
    { person | age = person.age + 1 }
    add x y =
    x + y
    addTwo =
    add 2

    View Slide

  54. Self documenting

    View Slide

  55. Types
    add : Int -> Int -> Int
    add x y =
    x + y
    isEven : Int -> Bool
    isEven x =
    n % 2 == 0

    View Slide

  56. Union Types
    type Filter
    = ShowAll
    | ShowCompleted
    | ShowActive

    View Slide

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

    View Slide

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

    View Slide

  59. Type aliases
    type alias Person =
    { name : String
    , age : Int
    }
    incrementAge : Person -> Person
    incrementAge person =
    { person | person.age = person.age + 1 }

    View Slide

  60. ! Clearer code, typed in your domain specific
    objects.
    ! Compiler can guarantee you're meeting the type
    requirements.
    ! No more 'undefined is not a function' !

    View Slide

  61. Robust

    View Slide

  62. Immutability brings guarantees
    var person = { name: 'Jack', age: 24 };
    incrementAge(person);
    // has this mutated?
    // does it return a new person?

    View Slide

  63. View Slide

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

    View Slide

  65. Pure Functions
    Elm functions are always pure.
    let sum = (a, b) => a + b; //PURE
    sum(2, 2) // => ALWAYS 4
    let otherSum = (a, b) => window.foo + a + b; //IMPURE
    otherSum(2, 2) // => who knows, dependent on window.foo

    View Slide

  66. Dealing with nothing

    View Slide

  67. I call it my billion-dollar
    mistake.

    View Slide

  68. It was the invention of the
    null reference in 1965.

    View Slide

  69. This has led to innumerable errors, vulnerabilities,
    and system crashes
    -- Tony Hoare

    View Slide

  70. You might say
    it's a...

    View Slide

  71. ...hoare-able
    mistake!

    View Slide

  72. (You may
    leave)

    View Slide

  73. No null: how do you
    represent a value being
    present or empty?

    View Slide

  74. render() {
    return { this.props.apiData.username };
    }

    View Slide

  75. Maybe

    View Slide

  76. Maybe
    type Maybe a =
    Just a
    | Nothing
    It's either Just some value, or Nothing.

    View Slide

  77. As a value, I am:
    → Just the integer 5
    → or, I am Nothing
    I have the type Maybe Int

    View Slide

  78. Get the first thing from the list, and double it2
    let list = [1, 2, 3] in
    (List.head list) * 2
    But what if list = [] ?
    2 this code is not valid Elm, becuase List.head returns Maybe

    View Slide


  79. View Slide

  80. 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) ]

    View Slide

  81. You must handle all cases
    of missing / pending data

    View Slide

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

    View Slide

  83. Commands
    an async thing that Elm should run for you (HTTP
    requests)

    View Slide

  84. Learning curve!

    View Slide

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

    View Slide

  86. View Slide

  87. View Slide

  88. Building actual apps in
    Elm

    View Slide

  89. The Elm Architecture

    View Slide

  90. The three parts:
    model : Model
    view : Model -> Html Msg
    update : Msg -> Model -> Model

    View Slide

  91. What changed?

    View Slide

  92. View Slide

  93. Building a counter app

    View Slide

  94. First, define your Model
    type alias Model = Int
    initialModel : Model
    initialModel = 0

    View Slide

  95. Secondly, define your Msgs
    type Msg = Increment | Decrement

    View Slide

  96. Thirdly, define your update:
    update : Msg -> Model -> Model
    update msg model =
    case msg of
    Increment -> model + 1
    Decrement -> model - 1

    View Slide

  97. Fourthly, define your view:
    view : Model -> Html Msg
    view model =
    div []
    [ button [ onClick Decrement ] [ text "-" ]
    , div [] [ text (toString model) ]
    , button [ onClick Increment ] [ text "+" ]
    ]

    View Slide

  98. Finally, hook it all up!
    main =
    Html.App.beginnerProgram
    { model = initialModel
    , view = view
    , update = update
    }

    View Slide

  99. View Slide

  100. ! We left the view until last.
    ! Explained all our logic before the UI.
    ! Notice how easy update would be to test.

    View Slide

  101. Side Effects

    View Slide

  102. Explicitly model side effects.
    Hand off to Elm, it will hand back later.
    Keeps functions pure, and async easier to reason
    about.

    View Slide

  103. Commands

    View Slide

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

    View Slide

  105. The Elm Architecture,
    Part 2

    View Slide

  106. model : Model
    view : Model -> Html Msg
    update : Msg -> Model -> (Model, Cmd Msg)

    View Slide

  107. View Slide

  108. Fetching someone's
    GitHub data.

    View Slide

  109. Firstly, define the model:
    type alias GithubPerson =
    { name : String
    , company : String
    }
    type alias Model =
    { username : String
    , githubPerson : Maybe GithubPerson
    }

    View Slide

  110. Secondly, define your Msgs
    type Msg
    = NewGithubData GithubPerson
    | FetchGithubData
    | FetchError Http.Error

    View Slide

  111. Thirdly, define your update:
    update : Msg -> Model -> ( Model, Cmd Msg )
    update msg model =
    case msg of
    NewGithubData person ->
    ( { model | githubPerson = Just person }, Cmd.none )
    FetchGithubData ->
    ( model, fetchGithubData model.username )
    ...

    View Slide

  112. NewGithubData person ->
    ( { model | githubPerson = Just person }, Cmd.none )
    -- Cmd.none === do nothing

    View Slide

  113. FetchGithubData ->
    ( model, fetchGithubData model.username )
    --- fetchGithubData returns a command
    --- which Elm will run for us

    View Slide

  114. View Slide

  115. 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!" ]
    ]

    View Slide

  116. Fifthly (new step), define your init:
    initialModel : Model
    initialModel =
    { username = "jackfranklin"
    , githubPerson = Nothing
    }
    init : ( Model, Cmd Msg )
    init =
    ( initialModel, Cmd.none )

    View Slide

  117. Finally, hook it all together!
    main =
    Html.App.program
    { init = init
    , view = view
    , update = update
    , subscriptions = \_ -> Sub.none
    }

    View Slide

  118. View Slide

  119. View Slide

  120. View Slide

  121. View Slide

  122. View Slide

  123. View Slide

  124. View Slide

  125. View Slide

  126. View Slide

  127. View Slide

  128. Deep breath!

    View Slide

  129. That feels like a lot of code to do something so
    simple
    -- All of you.

    View Slide

  130. Boilerplate vs
    Explicitness

    View Slide

  131. Benefits increase as application grows
    Which is often where JavaScript starts to struggle.

    View Slide

  132. jackfranklin/elm-for-js-developers-talk

    View Slide

  133. Components...ish?

    View Slide

  134. View Slide

  135. The Elm Ecosystem

    View Slide

  136. elm reactor

    View Slide

  137. elm package

    View Slide

  138. View Slide

  139. There's so much more I
    haven't covered.

    View Slide

  140. So, why / when should
    you use Elm?

    View Slide

  141. You're fed up of undefined
    function errors that take
    up loads of time

    View Slide

  142. You want to develop with
    the confidence of Types
    and a clever compiler to
    back you up

    View Slide

  143. You're happy to "ride the
    wave" and deal with a
    language still growing
    and settling down

    View Slide

  144. You're happy to build
    more packages than
    depend on existing
    solutions which may not
    exist in Elm

    View Slide

  145. But what if this talk has
    put me off Elm?

    View Slide

  146. Elm does take time to
    learn, so please don't
    give up after 30 minutes
    of slides!
    guide.elm-lang.org

    View Slide

  147. Elm the language brings
    many concepts that are
    language agnostic

    View Slide

  148. The Elm Architecture

    View Slide

  149. Explicitness across your
    application

    View Slide

  150. Types

    View Slide

  151. Immutability / Functional
    Programming

    View Slide

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

    View Slide

  153. Will everyone be writing
    Elm in 1/2/5 years?

    View Slide

  154. It Depends

    View Slide

  155. But it's great fun!

    View Slide

  156. → guide.elm-lang.org
    → elm-lang.org/docs
    → elm-lang.org/community
    → github.com/jackfranklin/elm-for-js-developers-
    talk
    → speakerdeck.com/jackfranklin

    View Slide

  157. @Jack_Franklin
    javascriptplayground.com

    View Slide