Pro Yearly is on sale from $80 to $50! »

JSCamp Romania: Elm for JS Developers

JSCamp Romania: Elm for JS Developers

Aea964cf59c0c81fff752896f070cbbb?s=128

Jack Franklin

June 07, 2016
Tweet

Transcript

  1. None
  2. !

  3. Good morning Buchapest!

  4. @Jack_Franklin

  5. None
  6. None
  7. None
  8. The great tooling problem

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

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

    truly easy
  11. Trends in Complex JavaScript Applications

  12. Two Way Data Binding

  13. Object.observe

  14. MVC / MVVC / MCVCVMMCVCCC

  15. "Let's replicate Rails"

  16. We can do better

  17. Component based approach

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

    » React
  20. Who actually likes JavaScript these days?!

  21. Explicit about state

  22. Single source of truth

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

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

    view(state2) => HTML2
  25. Explicitly define all actions that can modify state function addTodo()

    { return { type: 'USER_ADD_TODO', text: 'Buy Milk' } }
  26. Have update functions that can handle actions update(action, state) =>

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

    => newState1
  28. update encapsulates most business logic

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

    -> view(newState)
  30. None
  31. Side Effects

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

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

  34. user clicks -> action -> update(action, state) => (newState, cmdA)

    -> view(newState) (cmdARuns) -> cmdA returns -> update(action, state) => (newestState, none) -> view(newestState) || no commands
  35. Explicit vs Magic

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

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

    return Object.assign({}, state, { user: { name: action.name } }); } }
  38. Even more explicit! type Msg = NewUser String | LogOut

  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!
  40. None
  41. Elm: a language to solve these problems.

  42. Elm, my Dear Watson -- Sherlock Holmes

  43. Not the finished article

  44. Not the perfect language (yet?!)

  45. No runtime errors!

  46. » Functional » Typed » Compiled

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

  48. Learning curve ahead!

  49. Expressive, clear code

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

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

    List.map ((+) 2) [1, 2, 3, 4]
  52. Pipes incrementWeight (incrementHeight (incrementAge (makePerson "jack"))) makePerson "jack" |> incrementAge

    |> incrementHeight |> incrementWeight
  53. Clean syntax incrementAge person = { person | age =

    person.age + 1 } add x y = x + y addTwo = add 2
  54. Self documenting

  55. Types Dynamic languages are a foolish friend.

  56. !♥ ❤ ❤!

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

    Int -> Bool
  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
  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!
  60. Type variables someFunc : a -> b -> a someFunc

    : Int -> Bool -> Int someFunc : String -> Bool -> String someFunc : String -> Int -> String
  61. Type aliases type alias Person = { name : String

    , age : Int } incrementAge : Person -> Person incrementAge person = { person | person.age = person.age + 1 }
  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' !
  63. type alias Person = { name : String , age

    : Int }
  64. Robust

  65. Immutability brings guarantees var person = { name: 'Jack', age:

    24 }; incrementAge(person); // has this mutated? // does it return a new person? // #javascript
  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
  67. Modules ! Everything is scoped ! Modules explicitly declare what

    they expose ! Modules explicitly declare what they import
  68. Dealing with nothing No more null.

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

    either Just some value, or Nothing.
  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) ]
  71. You must handle all cases of missing / pending data

  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
  73. You have to deal with errors. Task doesn't let you

    not. (We'll come back to this later).
  74. Reactivity » Data changes » Async activities

  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
  76. Adjustment time This does take time to get used to

    » Syntax » Types » Immutablility » Compiling! » Maybe and explicit error handling
  77. Elm: no runtime errors

  78. user clicks -> action -> update(action, state) -> view(newState) ||

    run command -> command causes new action -> update(action, state) -> view(newState) || no commands
  79. The Elm Architecture

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

    Html Msg update : Msg -> Model -> Model
  81. Counter

  82. Live coding... !

  83. Use these slides if the live coding goes dreadfully And

    Jack failed miserably. (Check if anyone's left in the room).
  84. First, define your Model type alias Model = Int initialModel

    : Model initialModel = 0
  85. Secondly, define your Msgs type Msg = Increment | Decrement

  86. Thirdly, define your update: update : Msg -> Model ->

    Model update msg model = case msg of Increment -> model + 1 Decrement -> model - 1
  87. Fourthly, define your view: view : Model -> Html Msg

    view model = div [] [ button [ onClick Decrement ] [ text "-" ] , div [] [ text (toString model) ] , button [ onClick Increment ] [ text "+" ] ]
  88. Finally, hook it all up! main = Html.App.beginnerProgram { model

    = initialModel , view = view , update = update }
  89. ! We left the view until last. ! Explained all

    our logic before the UI. ! Notice how easy update would be to test.
  90. Commands

  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.
  92. Fetching someone's GitHub data.

  93. Firstly, define the model: type alias GithubPerson = { name

    : String , company : String } type alias Model = { username : String , githubPerson : Maybe GithubPerson }
  94. Secondly, define your Msgs type Msg = NewGithubData GithubPerson |

    FetchGithubData | FetchError Http.Error
  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 )
  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!" ] ]
  97. Fifthly (new step), define your init: initialModel : Model initialModel

    = { username = "jackfranklin" , githubPerson = Nothing } init : ( Model, Cmd Msg ) init = ( initialModel, Cmd.none )
  98. Finally, hook it all together! main = Html.App.program { init

    = init , view = view , update = update , subscriptions = \_ -> Sub.none }
  99. None
  100. That feels like a lot of code! -- All of

    you.
  101. Boilerplate vs Explicitness

  102. Benefits increase as application grows

  103. Fetching Data

  104. Decoding JSON githubDecoder : Json.Decoder GithubPerson githubDecoder = Json.object2 GithubPerson

    ("name" := Json.string) ("company" := Json.string)
  105. Making the request fetchGithubData : String -> Cmd Msg fetchGithubData

    username = Http.get githubDecoder (apiUrl username) |> Task.perform FetchError NewGithubData
  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
  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.
  108. github.com/jackfranklin/elm-for-js- developers

  109. Scaling your application

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

  111. The Elm Ecosystem

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

    no tooling required.
  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
  114. elm format

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

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

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

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

  119. You want to develop with the confidence of Types and

    a clever compiler to back you up
  120. You're happy to "ride the wave" and deal with a

    language still growing and settling down
  121. You're happy to build more packages than depend on existing

    solutions which may not exist in Elm
  122. But what if this talk has put me off Elm?

  123. Elm does take time to learn, so please don't give

    up after 30 minutes of slides! guide.elm-lang.org
  124. Elm the language brings many concepts that are language agnostic

  125. The Elm Architecture

  126. Explicitness across your application

  127. Types

  128. Immutability / Functional Programming

  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.
  130. Will everyone be writing Elm in 1/2/5 years?

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

  133. @Jack_Franklin javascriptplayground.com

  134. ! Thank you !