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

  2. Explicitly define all actions that can modify state function addTodo()

    { return { type: 'USER_ADD_TODO', text: 'Buy Milk' } }
  3. Async actions user clicks -> asyncActionFires -> update(action, state) ->

    view(newState) -> asyncActionReturns -> update(action, state) -> view(newState)
  4. user clicks -> action -> update(action, state) => (newState, cmdA)

    -> view(newState) (cmdARuns) -> cmdA returns -> update(action, state) => (newestState, none) -> view(newestState) || no commands
  5. Explicit! function update(action, state) { switch (action.type) { case 'NEW_USER':

    return Object.assign({}, state, { user: { name: action.name } }); } }
  6. 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!
  7. List.map (\x -> x + 2) [1, 2, 3, 4]

    List.map ((+) 2) [1, 2, 3, 4]
  8. Clean syntax incrementAge person = { person | age =

    person.age + 1 } add x y = x + y addTwo = add 2
  9. 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
  10. 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!
  11. Type variables someFunc : a -> b -> a someFunc

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

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

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

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

    they expose ! Modules explicitly declare what they import
  17. Maybe type Maybe a = Just a | Nothing It's

    either Just some value, or Nothing.
  18. 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) ]
  19. 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
  20. You have to deal with errors. Task doesn't let you

    not. (We'll come back to this later).
  21. 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
  22. Adjustment time This does take time to get used to

    » Syntax » Types » Immutablility » Compiling! » Maybe and explicit error handling
  23. user clicks -> action -> update(action, state) -> view(newState) ||

    run command -> command causes new action -> update(action, state) -> view(newState) || no commands
  24. The three parts: model : Model view : Model ->

    Html Msg update : Msg -> Model -> Model
  25. Use these slides if the live coding goes dreadfully And

    Jack failed miserably. (Check if anyone's left in the room).
  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. 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.
  31. Firstly, define the model: type alias GithubPerson = { name

    : String , company : String } type alias Model = { username : String , githubPerson : Maybe GithubPerson }
  32. 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 )
  33. 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!" ] ]
  34. Fifthly (new step), define your init: initialModel : Model initialModel

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

    = init , view = view , update = update , subscriptions = \_ -> Sub.none }
  36. Making the request fetchGithubData : String -> Cmd Msg fetchGithubData

    username = Http.get githubDecoder (apiUrl username) |> Task.perform FetchError NewGithubData
  37. 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
  38. 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.
  39. 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
  40. You want to develop with the confidence of Types and

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

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

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