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.

Aea964cf59c0c81fff752896f070cbbb?s=128

Jack Franklin

June 30, 2016
Tweet

Transcript

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  18. Components in some form • Angular 2 • Ember •

    Vue.js • Cycle.js • React @Jack_Franklin, bit.ly/elm-polyconf 18
  19. Who actually likes JavaScript these days?! @Jack_Franklin, bit.ly/elm-polyconf 19

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

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

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

  23. View functions are pure view(state1) => HTML1 view(state1) => HTML1

    view(state2) => HTML2 @Jack_Franklin, bit.ly/elm-polyconf 23
  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
  25. Have update functions that can handle actions update(action, state) =>

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

  27. Unidirectional Data Flow user clicks -> action -> update(action, state)

    -> view(newState) @Jack_Franklin, bit.ly/elm-polyconf 27
  28. Recommended Reading ➡ Unidirectional User Interface Architectures by André Staltz

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

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

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

    }; } @Jack_Franklin, bit.ly/elm-polyconf 31
  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
  33. Even more explicit! type Msg = NewUser String | LogOut

    @Jack_Franklin, bit.ly/elm-polyconf 33
  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
  35. @Jack_Franklin, bit.ly/elm-polyconf 35

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

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

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

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

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

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

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

  43. • Expressive, clear code • Self documenting • Robust @Jack_Franklin,

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

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

  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
  47. Pipes incrementWeight (incrementHeight (incrementAge (makePerson "jack"))) makePerson "jack" |> incrementAge

    |> incrementHeight |> incrementWeight @Jack_Franklin, bit.ly/elm-polyconf 47
  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
  49. Self documenting @Jack_Franklin, bit.ly/elm-polyconf 49

  50. Types add : Int -> Int -> Int isEven :

    Int -> Bool @Jack_Franklin, bit.ly/elm-polyconf 50
  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
  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
  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
  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
  55. type alias Person = { name : String , age

    : Int } @Jack_Franklin, bit.ly/elm-polyconf 55
  56. Robust @Jack_Franklin, bit.ly/elm-polyconf 56

  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
  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
  59. Modules ! Everything is scoped ! Modules explicitly declare what

    they expose ! Modules explicitly declare what they import @Jack_Franklin, bit.ly/elm-polyconf 59
  60. Dealing with nothing No more null. @Jack_Franklin, bit.ly/elm-polyconf 60

  61. Maybe type Maybe a = Just a | Nothing It's

    either Just some value, or Nothing. @Jack_Franklin, bit.ly/elm-polyconf 61
  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
  63. You must handle all cases of missing / pending data

    @Jack_Franklin, bit.ly/elm-polyconf 63
  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
  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
  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
  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
  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
  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
  70. The Elm Architecture @Jack_Franklin, bit.ly/elm-polyconf 70

  71. The three parts: model : Model view : Model ->

    Html Msg update : Msg -> Model -> Model @Jack_Franklin, bit.ly/elm-polyconf 71
  72. @Jack_Franklin, bit.ly/elm-polyconf 72

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

  74. First, define your Model type alias Model = Int initialModel

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

    @Jack_Franklin, bit.ly/elm-polyconf 75
  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
  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
  78. Finally, hook it all up! main = Html.App.beginnerProgram { model

    = initialModel , view = view , update = update } @Jack_Franklin, bit.ly/elm-polyconf 78
  79. @Jack_Franklin, bit.ly/elm-polyconf 79

  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
  81. Side Effects @Jack_Franklin, bit.ly/elm-polyconf 81

  82. Explicitly model side effects. Hand off to Elm, it will

    hand back later. @Jack_Franklin, bit.ly/elm-polyconf 82
  83. Commands @Jack_Franklin, bit.ly/elm-polyconf 83

  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
  85. model : Model view : Model -> Html Msg update

    : Msg -> Model -> (Model, Cmd Msg) @Jack_Franklin, bit.ly/elm-polyconf 85
  86. @Jack_Franklin, bit.ly/elm-polyconf 86

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

  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
  89. Secondly, define your Msgs type Msg = NewGithubData GithubPerson |

    FetchGithubData | FetchError Http.Error @Jack_Franklin, bit.ly/elm-polyconf 89
  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
  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
  92. @Jack_Franklin, bit.ly/elm-polyconf 92

  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
  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
  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
  96. @Jack_Franklin, bit.ly/elm-polyconf 96

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

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

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

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

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

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

  103. That feels like a lot of code / effort! --

    All of you. @Jack_Franklin, bit.ly/elm-polyconf 103
  104. Boilerplate vs Explicitness @Jack_Franklin, bit.ly/elm-polyconf 104

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

  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
  107. http://bit.ly/elm-polyconf @Jack_Franklin, bit.ly/elm-polyconf 107

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

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

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

  111. elm reactor Easily run a project in the browser with

    no tooling required. @Jack_Franklin, bit.ly/elm-polyconf 111
  112. @Jack_Franklin, bit.ly/elm-polyconf 112

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

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

  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
  116. elm format @Jack_Franklin, bit.ly/elm-polyconf 116

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

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

    118
  119. You're fed up of undefined function errors that take up

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

    @Jack_Franklin, bit.ly/elm-polyconf 120
  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
  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
  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
  124. But what if this talk has put me off Elm?

    @Jack_Franklin, bit.ly/elm-polyconf 124
  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
  126. Elm the language brings many concepts that are language agnostic

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

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

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

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

  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
  132. Will everyone be writing Elm in 1/2/5 years? @Jack_Franklin, bit.ly/elm-polyconf

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

  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
  135. @Jack_Franklin javascriptplayground.com @Jack_Franklin, bit.ly/elm-polyconf 135

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