FullStackFest: Elm for JS Developers

Aea964cf59c0c81fff752896f070cbbb?s=47 Jack Franklin
September 08, 2016

FullStackFest: Elm for JS Developers

Aea964cf59c0c81fff752896f070cbbb?s=128

Jack Franklin

September 08, 2016
Tweet

Transcript

  1. 1.
  2. 2.

    !

  3. 3.
  4. 4.
  5. 5.
  6. 8.
  7. 9.
  8. 10.
  9. 16.
  10. 17.
  11. 21.

    http://todo.com/filter/completed { todos: [{ ... }, { ... }], filter:

    'completed' } Duplicate knowledge = out of sync quickly!
  12. 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; }); } });
  13. 32.

    function addTodo() { return { type: 'USER_ADD_TODO', text: 'Buy Milk'

    } } These can be logged, stored, tracked, serialized and so on.
  14. 33.

    Have update functions that can handle actions and update the

    state update(action, state) => newState
  15. 34.

    const action = { type: 'USER_LOG_OUT'; }; const currentState =

    { user: { name: 'Jack' } }; const newState = update(action, currentState); newState => { user: undefined };
  16. 38.
  17. 40.
  18. 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 } }); } }
  19. 43.
  20. 50.

    Expressive, clear code (add 1 2) List.map (\x -> x

    + 2) [1, 2, 3, 4] List.map ((+) 2) [1, 2, 3, 4]
  21. 52.

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

    incrementWeight |> incrementWeight |> incrementWeight |> incrementWeight |> incrementWeight |> incrementWeight |> incrementWeight |> incrementWeight |> incrementWeight |> incrementWeight
  22. 53.

    incrementAge person = { person | age = person.age +

    1 } add x y = x + y addTwo = add 2
  23. 55.

    Types add : Int -> Int -> Int add x

    y = x + y isEven : Int -> Bool isEven x = n % 2 == 0
  24. 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
  25. 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!
  26. 59.

    Type aliases type alias Person = { name : String

    , age : Int } incrementAge : Person -> Person incrementAge person = { person | person.age = person.age + 1 }
  27. 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' !
  28. 61.
  29. 62.

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

    24 }; incrementAge(person); // has this mutated? // does it return a new person?
  30. 63.
  31. 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
  32. 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
  33. 75.
  34. 76.

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

    either Just some value, or Nothing.
  35. 77.

    As a value, I am: → Just the integer 5

    → or, I am Nothing I have the type Maybe Int
  36. 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
  37. 79.

  38. 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) ]
  39. 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
  40. 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
  41. 86.
  42. 87.
  43. 90.

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

    Html Msg update : Msg -> Model -> Model
  44. 92.
  45. 96.

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

    Model update msg model = case msg of Increment -> model + 1 Decrement -> model - 1
  46. 97.

    Fourthly, define your view: view : Model -> Html Msg

    view model = div [] [ button [ onClick Decrement ] [ text "-" ] , div [] [ text (toString model) ] , button [ onClick Increment ] [ text "+" ] ]
  47. 98.

    Finally, hook it all up! main = Html.App.beginnerProgram { model

    = initialModel , view = view , update = update }
  48. 99.
  49. 100.

    ! We left the view until last. ! Explained all

    our logic before the UI. ! Notice how easy update would be to test.
  50. 102.

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

    hand back later. Keeps functions pure, and async easier to reason about.
  51. 103.
  52. 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.
  53. 106.

    model : Model view : Model -> Html Msg update

    : Msg -> Model -> (Model, Cmd Msg)
  54. 107.
  55. 109.

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

    : String , company : String } type alias Model = { username : String , githubPerson : Maybe GithubPerson }
  56. 110.
  57. 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 ) ...
  58. 112.

    NewGithubData person -> ( { model | githubPerson = Just

    person }, Cmd.none ) -- Cmd.none === do nothing
  59. 114.
  60. 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!" ] ]
  61. 116.

    Fifthly (new step), define your init: initialModel : Model initialModel

    = { username = "jackfranklin" , githubPerson = Nothing } init : ( Model, Cmd Msg ) init = ( initialModel, Cmd.none )
  62. 117.

    Finally, hook it all together! main = Html.App.program { init

    = init , view = view , update = update , subscriptions = \_ -> Sub.none }
  63. 118.
  64. 119.
  65. 120.
  66. 121.
  67. 122.
  68. 123.
  69. 124.
  70. 125.
  71. 126.
  72. 127.
  73. 129.
  74. 134.
  75. 138.
  76. 142.
  77. 143.

    You're happy to "ride the wave" and deal with a

    language still growing and settling down
  78. 144.
  79. 146.

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

    up after 30 minutes of slides! guide.elm-lang.org
  80. 150.
  81. 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.
  82. 154.