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

JSCamp Romania: Elm for JS Developers

JSCamp Romania: Elm for JS Developers

Jack Franklin

June 07, 2016
Tweet

More Decks by Jack Franklin

Other Decks in Technology

Transcript

  1. View Slide

  2. !

    View Slide

  3. Good morning Buchapest!

    View Slide

  4. @Jack_Franklin

    View Slide

  5. View Slide

  6. View Slide

  7. View Slide

  8. The great tooling problem

    View Slide

  9. We must accept that
    complex applications are
    hard to build

    View Slide

  10. And that no tool / language
    can ever make them truly
    easy

    View Slide

  11. Trends in Complex
    JavaScript Applications

    View Slide

  12. Two Way Data Binding

    View Slide

  13. Object.observe

    View Slide

  14. MVC / MVVC /
    MCVCVMMCVCCC

    View Slide

  15. "Let's replicate Rails"

    View Slide

  16. We can do better

    View Slide

  17. Component based
    approach

    View Slide

  18. View Slide

  19. Components
    » Angular 2 (thanks Tim!)
    » Ember
    » Vue.js
    » React

    View Slide

  20. Who actually likes
    JavaScript these days?!

    View Slide

  21. Explicit about state

    View Slide

  22. Single source of truth

    View Slide

  23. Views represent state
    view(state) => HTML

    View Slide

  24. View functions are pure
    view(state1) => HTML1
    view(state1) => HTML1
    view(state2) => HTML2

    View Slide

  25. Explicitly define all actions that
    can modify state
    function addTodo() {
    return {
    type: 'USER_ADD_TODO',
    text: 'Buy Milk'
    }
    }

    View Slide

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

    View Slide

  27. These are pure, too
    update(a1, s1) =>
    newState1
    update(a1, s1) =>
    newState1

    View Slide

  28. update encapsulates most
    business logic

    View Slide

  29. Unidirectional Data Flow
    user clicks
    -> action
    -> update(action, state)
    -> view(newState)

    View Slide

  30. View Slide

  31. Side Effects

    View Slide

  32. Async actions
    user clicks
    -> asyncActionFires
    -> update(action, state)
    -> view(newState)
    -> asyncActionReturns
    -> update(action, state)
    -> view(newState)

    View Slide

  33. Explicit Side Effects
    update(action, state) => (newState, command)

    View Slide

  34. user clicks
    -> action
    -> update(action, state) => (newState, cmdA)
    -> view(newState) (cmdARuns)
    -> cmdA returns
    -> update(action, state) => (newestState, none)
    -> view(newestState) || no commands

    View Slide

  35. Explicit vs Magic

    View Slide

  36. Magic!
    function setNewUser(name) {
    $scope.user = { name : 'jack' };
    }

    View Slide

  37. Explicit!
    function update(action, state) {
    switch (action.type) {
    case 'NEW_USER':
    return Object.assign({}, state, {
    user: { name: action.name }
    });
    }
    }

    View Slide

  38. Even more explicit!
    type Msg =
    NewUser String
    | LogOut

    View Slide

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

    View Slide

  40. View Slide

  41. Elm: a language to solve
    these problems.

    View Slide

  42. Elm, my Dear Watson
    -- Sherlock Holmes

    View Slide

  43. Not the finished article

    View Slide

  44. Not the perfect language
    (yet?!)

    View Slide

  45. No runtime errors!

    View Slide

  46. » Functional
    » Typed
    » Compiled

    View Slide

  47. » Expressive, clear code
    » Self documenting
    » Robust

    View Slide

  48. Learning curve ahead!

    View Slide

  49. Expressive, clear code

    View Slide

  50. Functional Programming
    add(1, 2)
    (add 1 2)

    View Slide

  51. List.map (\x -> x + 2) [1, 2, 3, 4]
    List.map ((+) 2) [1, 2, 3, 4]

    View Slide

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

    View Slide

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

    View Slide

  54. Self documenting

    View Slide

  55. Types
    Dynamic languages are a foolish friend.

    View Slide

  56. !♥ ❤ ❤!

    View Slide

  57. Types
    add : Int -> Int -> Int
    isEven : Int -> Bool

    View Slide

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

    View Slide

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

  60. Type variables
    someFunc : a -> b -> a
    someFunc : Int -> Bool -> Int
    someFunc : String -> Bool -> String
    someFunc : String -> Int -> String

    View Slide

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

    View Slide

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

  63. type alias Person =
    { name : String
    , age : Int
    }

    View Slide

  64. Robust

    View Slide

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

    View Slide

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

  67. Modules
    ! Everything is scoped
    ! Modules explicitly declare what they expose
    ! Modules explicitly declare what they import

    View Slide

  68. Dealing with nothing
    No more null.

    View Slide

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

    View Slide

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

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

    View Slide

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

  73. You have to deal with errors.
    Task doesn't let you not.
    (We'll come back to this later).

    View Slide

  74. Reactivity
    » Data changes
    » Async activities

    View Slide

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

    View Slide

  76. Adjustment time
    This does take time to get used to
    » Syntax
    » Types
    » Immutablility
    » Compiling!
    » Maybe and explicit error handling

    View Slide

  77. Elm: no runtime errors

    View Slide

  78. user clicks
    -> action
    -> update(action, state)
    -> view(newState) || run command
    -> command causes new action
    -> update(action, state)
    -> view(newState) || no commands

    View Slide

  79. The Elm Architecture

    View Slide

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

    View Slide

  81. Counter

    View Slide

  82. Live coding... !

    View Slide

  83. Use these slides if the live
    coding goes dreadfully
    And Jack failed miserably.
    (Check if anyone's left in the room).

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

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

    View Slide

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

    View Slide

  90. Commands

    View Slide

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

  92. Fetching someone's
    GitHub data.

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

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

    View Slide

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

    View Slide

  99. View Slide

  100. That feels like a lot of code!
    -- All of you.

    View Slide

  101. Boilerplate vs Explicitness

    View Slide

  102. Benefits increase as
    application grows

    View Slide

  103. Fetching Data

    View Slide

  104. Decoding JSON
    githubDecoder : Json.Decoder GithubPerson
    githubDecoder =
    Json.object2 GithubPerson
    ("name" := Json.string)
    ("company" := Json.string)

    View Slide

  105. Making the request
    fetchGithubData : String -> Cmd Msg
    fetchGithubData username =
    Http.get githubDecoder (apiUrl username)
    |> Task.perform FetchError NewGithubData

    View Slide

  106. Http.get : Json.Decode.Decoder a -> String -> Task Http.Error a
    » takes a decoder that decodes into type a and a
    string (the URL)
    » returns a Task that either fails with Http.Error
    or succeeds with type a

    View Slide

  107. Task.perform : (a -> Msg) -> (c -> Msg) -> Task a c -> Cmd Msg
    Task.perform : errorHander successHandler task
    » takes a task that will fail or succeed
    » takes an error function that can convert the
    failure to a Msg
    » takes a success function that can convert the
    success to a Msg
    » Returns a Cmd that will perform the task in the
    background.

    View Slide

  108. github.com/jackfranklin/elm-for-js-
    developers

    View Slide

  109. Scaling your application

    View Slide

  110. It's just components all the
    way down!

    View Slide

  111. The Elm Ecosystem

    View Slide

  112. elm reactor
    Easily run a project in the browser with no tooling
    required.

    View Slide

  113. elm package
    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

    View Slide

  114. elm format

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  118. You're fed up of packages on
    npm breaking semantic
    versioning

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  125. The Elm Architecture

    View Slide

  126. Explicitness across your
    application

    View Slide

  127. Types

    View Slide

  128. Immutability / Functional
    Programming

    View Slide

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

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

    View Slide

  131. View Slide

  132. » javascriptplayground.com/elm-jscamp.html
    » guide.elm-lang.org
    » elm-lang.org/docs
    » elm-lang.org/community

    View Slide

  133. @Jack_Franklin
    javascriptplayground.com

    View Slide

  134. ! Thank you !

    View Slide