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

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

    'completed' } Duplicate knowledge = out of sync quickly!
  3. 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; }); } });
  4. function addTodo() { return { type: 'USER_ADD_TODO', text: 'Buy Milk'

    } } These can be logged, stored, tracked, serialized and so on.
  5. Have update functions that can handle actions and update the

    state update(action, state) => newState
  6. const action = { type: 'USER_LOG_OUT'; }; const currentState =

    { user: { name: 'Jack' } }; const newState = update(action, currentState); newState => { user: undefined };
  7. 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 } }); } }
  8. Expressive, clear code (add 1 2) List.map (\x -> x

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

    incrementWeight |> incrementWeight |> incrementWeight |> incrementWeight |> incrementWeight |> incrementWeight |> incrementWeight |> incrementWeight |> incrementWeight |> incrementWeight
  10. incrementAge person = { person | age = person.age +

    1 } add x y = x + y addTwo = add 2
  11. Types add : Int -> Int -> Int add x

    y = x + y isEven : Int -> Bool isEven x = n % 2 == 0
  12. 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
  13. 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!
  14. Type aliases type alias Person = { name : String

    , age : Int } incrementAge : Person -> Person incrementAge person = { person | person.age = person.age + 1 }
  15. ! Clearer code, typed in your domain specific objects. !

    Compiler can guarantee you're meeting the type requirements. ! No more 'undefined is not a function' !
  16. Immutability brings guarantees var person = { name: 'Jack', age:

    24 }; incrementAge(person); // has this mutated? // does it return a new person?
  17. 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
  18. 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
  19. Maybe type Maybe a = Just a | Nothing It's

    either Just some value, or Nothing.
  20. As a value, I am: → Just the integer 5

    → or, I am Nothing I have the type Maybe Int
  21. 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
  22. 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) ]
  23. 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
  24. 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
  25. The three parts: model : Model view : Model ->

    Html Msg update : Msg -> Model -> Model
  26. Thirdly, define your update: update : Msg -> Model ->

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

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

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

    our logic before the UI. ! Notice how easy update would be to test.
  30. Explicitly model side effects. Hand off to Elm, it will

    hand back later. Keeps functions pure, and async easier to reason about.
  31. 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.
  32. model : Model view : Model -> Html Msg update

    : Msg -> Model -> (Model, Cmd Msg)
  33. Firstly, define the model: type alias GithubPerson = { name

    : String , company : String } type alias Model = { username : String , githubPerson : Maybe GithubPerson }
  34. 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 ) ...
  35. NewGithubData person -> ( { model | githubPerson = Just

    person }, Cmd.none ) -- Cmd.none === do nothing
  36. 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!" ] ]
  37. Fifthly (new step), define your init: initialModel : Model initialModel

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

    = init , view = view , update = update , subscriptions = \_ -> Sub.none }
  39. You're happy to "ride the wave" and deal with a

    language still growing and settling down
  40. Elm does take time to learn, so please don't give

    up after 30 minutes of slides! guide.elm-lang.org
  41. 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.