Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Codemash 2017: Toward a Better Front-end Architecture: Elm

Codemash 2017: Toward a Better Front-end Architecture: Elm

Jeremy Fairbank

January 12, 2017
Tweet

More Decks by Jeremy Fairbank

Other Decks in Programming

Transcript

  1. TOWARD A BETTER
    |> FRONT END
    |> ARCHITECTURE
    elm
    Jeremy Fairbank
    blog.jeremyfairbank.com
    tw: @elpapapollo / gh: jfairbank

    View full-size slide

  2. sigient.com
    Your website, SimplyBuilt.
    simplybuilt.com

    View full-size slide

  3. ×
    two-way
    data binding
    mvc

    View full-size slide

  4. ×

    two-way
    data binding
    unidirectional
    mvc
    components

    View full-size slide

  5. null
    undefined is not a function

    View full-size slide

  6. let name = 'Jeremy';
    export function getName() {
    return name;
    }
    export function setName(newName) {
    name = newName;
    }
    Impure and Mutable

    View full-size slide

  7. Unsafe APIs
    axios.get(`/user/${id}`)
    .then((user) => {
    console.log(user.name);
    })
    .catch((error) => {
    console.log(error);
    });

    View full-size slide

  8. Unsafe APIs
    axios.get(`/user/${id}`)
    .then((user) => {
    console.log(user.name);
    })
    .catch((error) => {
    console.log(error);
    });

    View full-size slide

  9. Error Handling
    function mayThrow() {
    throw new Error('Whoops...')
    }
    function unsafe() {
    try {
    mayThrow();
    } catch (e) {
    handleError(e);
    }
    }
    Call Stack
    ×

    View full-size slide

  10. axios.get(`/user/${id}`)
    .then((user) => {
    console.log(user.name);
    });
    Error Handling

    View full-size slide

  11. axios.get(`/user/${id}`)
    .then((user) => {
    console.log(user.name);
    });
    Error Handling
    catch?

    View full-size slide

  12. No runtime errors in practice.
    No null.
    No undefined is not a function.
    - guide.elm-lang.org

    View full-size slide

  13. confidence
    Static typing
    Expressive
    Terse
    Pure
    Unidirectional
    Architecture
    Immutable

    View full-size slide

  14. greet name = "Hello, " ++ name
    add x y = x + y
    greet "Codemash" -- Hello, Codemash
    add 2 3 -- 5
    functional

    View full-size slide

  15. greet name = "Hello, " ++ name
    add x y = x + y
    greet "Codemash" -- Hello, Codemash
    add 2 3 -- 5
    functional

    View full-size slide

  16. greet name = "Hello, " ++ name
    add x y = x + y
    greet "Codemash" -- Hello, Codemash
    add 2 3 -- 5
    functional

    View full-size slide

  17. greet name = "Hello, " ++ name
    add x y = x + y
    greet "Codemash" -- Hello, Codemash
    add 2 3 -- 5
    functional

    View full-size slide

  18. greet name = "Hello, " ++ name
    add x y = x + y
    greet "Codemash" -- Hello, Codemash
    add 2 3 -- 5
    functional

    View full-size slide

  19. greet name = "Hello, " ++ name
    add x y = x + y
    greet "Codemash" -- Hello, Codemash
    add 2 3 -- 5
    functional

    View full-size slide

  20. pure
    add x y =
    x + y
    add 2 3 -- 5
    add 2 3 -- 5
    add 2 3 -- 5

    View full-size slide

  21. pure
    add x y =
    x + y
    add 2 3 -- 5
    add 2 3 -- 5
    add 2 3 -- 5
    Referentially
    Transparent

    View full-size slide

  22. function fetchUser(id) {
    const url = `/user/${id}`;
    return axios.get(url);
    }
    pure

    ×
    fetchUser id =
    let
    url = "/user/" ++ (toString id)
    request = Http.get url userDecoder
    in
    Http.send LoadUser request

    View full-size slide

  23. declarative
    Declare what the
    desired result is

    View full-size slide

  24. function doubleNumbers(numbers) {
    const doubled = [];
    const l = numbers.length;
    for (let i = 0; i < l; i++) {
    doubled.push(numbers[i] * 2);
    }
    return doubled;
    }
    doubleNumbers([1, 2, 3, 4, 5]);
    // [2, 4, 6, 8, 10]
    imperative

    View full-size slide

  25. function doubleNumbers(numbers) {
    const doubled = [];
    const l = numbers.length;
    for (let i = 0; i < l; i++) {
    doubled.push(numbers[i] * 2);
    }
    return doubled;
    }
    doubleNumbers([1, 2, 3, 4, 5]);
    // [2, 4, 6, 8, 10]
    imperative
    ×

    View full-size slide

  26. myList = [1, 2, 3, 4, 5]
    double n = n * 2
    doubleNumbers list =
    List.map double list
    doubleNumbers myList -- [2, 4, 6, 8, 10]
    declarative

    View full-size slide

  27. myList = [1, 2, 3, 4, 5]
    double n = n * 2
    doubleNumbers list =
    List.map double list
    doubleNumbers myList -- [2, 4, 6, 8, 10]
    declarative

    View full-size slide

  28. myList = [1, 2, 3, 4, 5]
    double n = n * 2
    doubleNumbers list =
    List.map double list
    doubleNumbers myList -- [2, 4, 6, 8, 10]
    declarative

    View full-size slide

  29. myList = [1, 2, 3, 4, 5]
    double n = n * 2
    doubleNumbers list =
    List.map double list
    doubleNumbers myList -- [2, 4, 6, 8, 10]
    declarative

    View full-size slide

  30. currying
    Creating building
    blocks

    View full-size slide

  31. currying
    add x y z = x + y + z
    add 1 2 3 -- 6
    ((add 1) 2) 3 -- 6
    add1 = add 1
    add3 = add1 2
    add1 2 3 -- 6
    add3 3 -- 6

    View full-size slide

  32. currying
    add x y z = x + y + z
    add 1 2 3 -- 6
    ((add 1) 2) 3 -- 6
    add1 = add 1
    add3 = add1 2
    add1 2 3 -- 6
    add3 3 -- 6

    View full-size slide

  33. currying
    add x y z = x + y + z
    add 1 2 3 -- 6
    ((add 1) 2) 3 -- 6
    add1 = add 1
    add3 = add1 2
    add1 2 3 -- 6
    add3 3 -- 6

    View full-size slide

  34. currying
    add x y z = x + y + z
    add 1 2 3 -- 6
    ((add 1) 2) 3 -- 6
    add1 = add 1
    add3 = add1 2
    add1 2 3 -- 6
    add3 3 -- 6

    View full-size slide

  35. currying
    double n = n * 2
    doubleNumbers list =
    List.map double list

    View full-size slide

  36. currying
    double n = n * 2
    doubleNumbers =
    List.map double

    View full-size slide

  37. currying
    double = ((*) 2)
    doubleNumbers =
    List.map double

    View full-size slide

  38. currying
    double = ((*) 2)
    doubleNumbers =
    List.map double

    View full-size slide

  39. currying doubleNumbers =
    List.map ((*) 2)

    View full-size slide

  40. list = List.range 1 10
    square n = n * n
    List.map square (List.filter ((<) 6) (List.map ((*) 2) list))
    piping

    View full-size slide

  41. list = List.range 1 10
    square n = n * n
    List.map square (List.filter ((<) 6) (List.map ((*) 2) list))
    piping

    View full-size slide

  42. piping
    list = List.range 1 10
    square n = n * n
    list
    |> List.map ((*) 2)
    |> List.filter ((<) 6)
    |> List.map square

    View full-size slide

  43. piping
    list = List.range 1 10
    square n = n * n
    list
    |> List.map ((*) 2)
    |> List.filter ((<) 6)
    |> List.map square

    View full-size slide

  44. |> List.map ((*) 2)
    |> List.filter ((<) 6)
    |> List.map square
    piping
    list

    View full-size slide

  45. |> List.map ((*) 2)
    |> List.filter ((<) 6)
    |> List.map square
    piping
    list
    doubled = List.map ((*) 2) list

    View full-size slide

  46. |> List.filter ((<) 6)
    |> List.map square
    piping
    doubled = List.map ((*) 2) list
    doubled

    View full-size slide

  47. |> List.filter ((<) 6)
    |> List.map square
    piping
    doubled = List.map ((*) 2) list
    doubled
    filtered = List.filter ((<) 6) mapped

    View full-size slide

  48. piping
    doubled = List.map ((*) 2) list
    filtered = List.filter ((<) 6) mapped
    |> List.map square
    filtered

    View full-size slide

  49. piping
    doubled = List.map ((*) 2) list
    filtered = List.filter ((<) 6) mapped
    |> List.map square filtered
    squared = List.map square filtered

    View full-size slide

  50. strong static typing
    Creating and adhering
    to contracts

    View full-size slide

  51. life : Int
    life = 42
    greeting : String
    greeting = "Hello World"
    isTrue : Bool
    isTrue = True
    numbers : List Int
    numbers = [1, 2, 3]
    strong
    static
    typing

    View full-size slide

  52. strong
    static
    typing
    greet : String -> String
    greet name =
    "Hello, " ++ name
    add : Int -> Int -> Int
    add x y =
    x + y

    View full-size slide

  53. strong
    static
    typing
    greet : String -> String
    greet name =
    "Hello, " ++ name
    add : Int -> Int -> Int
    add x y =
    x + y

    View full-size slide

  54. strong
    static
    typing
    greet : String -> String
    greet name =
    "Hello, " ++ name
    add : Int -> Int -> Int
    add x y =
    x + y

    View full-size slide

  55. strong
    static
    typing
    greet : String -> String
    greet name =
    "Hello, " ++ name
    add : Int -> (Int -> Int)
    add x y =
    x + y

    View full-size slide

  56. greet True
    add 2 "5"

    View full-size slide

  57. dog : ( String, Int )
    dog = ( "Tucker", 11 )
    name = Tuple.first dog -- "Tucker"
    age = Tuple.second dog -- 11
    tuples

    View full-size slide

  58. dog : ( String, Int )
    dog = ( "Tucker", 11 )
    name = Tuple.first dog -- "Tucker"
    age = Tuple.second dog -- 11
    tuples

    View full-size slide

  59. dog : ( String, Int )
    dog = ( "Tucker", 11 )
    name = Tuple.first dog -- "Tucker"
    age = Tuple.second dog -- 11
    tuples

    View full-size slide

  60. dog : ( String, Int )
    dog = ( "Tucker", 11 )
    name = Tuple.first dog -- "Tucker"
    age = Tuple.second dog -- 11
    tuples

    View full-size slide

  61. records
    dog : { name : String, age : Int, breed : String }
    dog =
    { name = "Tucker"
    , age = 11
    , breed = "Sheltie"
    }
    dog.name -- "Tucker"
    dog.age -- 11
    dog.breed -- "Sheltie"
    .name dog -- "Tucker"

    View full-size slide

  62. records
    dog : { name : String, age : Int, breed : String }
    dog =
    { name = "Tucker"
    , age = 11
    , breed = "Sheltie"
    }
    dog.name -- "Tucker"
    dog.age -- 11
    dog.breed -- "Sheltie"
    .name dog -- "Tucker"

    View full-size slide

  63. records
    dog : { name : String, age : Int, breed : String }
    dog =
    { name = "Tucker"
    , age = 11
    , breed = "Sheltie"
    }
    dog.name -- "Tucker"
    dog.age -- 11
    dog.breed -- "Sheltie"
    .name dog -- "Tucker"

    View full-size slide

  64. records
    dog : { name : String, age : Int, breed : String }
    dog =
    { name = "Tucker"
    , age = 11
    , breed = "Sheltie"
    }
    dog.name -- "Tucker"
    dog.age -- 11
    dog.breed -- "Sheltie"
    .name dog -- "Tucker"

    View full-size slide

  65. records
    dog : { name : String, age : Int, breed : String }
    dog =
    { name = "Tucker"
    , age = 11
    , breed = "Sheltie"
    }
    dog.name -- "Tucker"
    dog.age -- 11
    dog.breed -- "Sheltie"
    .name dog -- "Tucker"

    View full-size slide

  66. type alias Dog =
    { name : String
    , age : Int
    , breed : String
    }
    dog : Dog
    dog =
    { name = "Tucker"
    , age = 11
    , breed = "Sheltie"
    }
    aliases

    View full-size slide

  67. type alias Dog =
    { name : String
    , age : Int
    , breed : String
    }
    dog : Dog
    dog =
    { name = "Tucker"
    , age = 11
    , breed = "Sheltie"
    }
    aliases

    View full-size slide

  68. type alias Dog =
    { name : String
    , age : Int
    , breed : String
    }
    dog : Dog
    dog =
    { name = "Tucker"
    , age = 11
    , breed = "Sheltie"
    }
    aliases

    View full-size slide

  69. aliases
    type alias Dog =
    { name : String
    , age : Int
    , breed : String
    }
    dog : Dog
    dog = Dog "Tucker" 11 "Sheltie"

    View full-size slide

  70. immutable
    Create state, don’t
    mutate it

    View full-size slide

  71. immutable
    dog = Dog "Tucker" 11 "Sheltie"
    olderDog = { dog | age = dog.age + 1 }
    dog.age -- 11
    olderDog.age -- 12

    View full-size slide

  72. immutable
    dog = Dog "Tucker" 11 "Sheltie"
    olderDog = { dog | age = dog.age + 1 }
    dog.age -- 11
    olderDog.age -- 12

    View full-size slide

  73. immutable
    dog = Dog "Tucker" 11 "Sheltie"
    olderDog = { dog | age = dog.age + 1 }
    dog.age -- 11
    olderDog.age -- 12

    View full-size slide

  74. immutable
    dog = Dog "Tucker" 11 "Sheltie"
    olderDog = { dog | age = dog.age + 1 }
    dog.age -- 11
    olderDog.age -- 12

    View full-size slide

  75. immutable
    dog = Dog "Tucker" 11 "Sheltie"
    olderDog = { dog | age = dog.age + 1 }
    dog.age -- 11
    olderDog.age -- 12

    View full-size slide

  76. union types
    type alias Dog =
    { name : String
    , age : Int
    , breed : String
    }

    View full-size slide

  77. union types
    type alias Dog =
    { name : String
    , age : Int
    , breed : String
    }

    View full-size slide

  78. type Breed
    = Sheltie
    | Corgi
    | GoldenRetriever
    | Mix Breed Breed
    union types
    type alias Dog =
    { name : String
    , age : Int
    , breed : String
    }

    View full-size slide

  79. type Breed
    = Sheltie
    | Corgi
    | GoldenRetriever
    | Mix Breed Breed
    union types
    type alias Dog =
    { name : String
    , age : Int
    , breed : Breed
    }

    View full-size slide

  80. type Breed
    = Sheltie
    | Corgi
    | GoldenRetriever
    | Mix Breed Breed
    union types

    View full-size slide

  81. type Breed
    = Sheltie
    | Corgi
    | GoldenRetriever
    | Mix Breed Breed
    union types

    View full-size slide

  82. type Breed
    = Sheltie
    | Corgi
    | GoldenRetriever
    | Mix Breed Breed
    union types

    View full-size slide

  83. type Breed
    = Sheltie
    | Corgi
    | GoldenRetriever
    | Mix Breed Breed
    dog1 = Dog "Tucker" 11 Sheltie
    dog2 = Dog "Sally" 5 Corgi
    dog3 = Dog "Rover" 1 Mix

    View full-size slide

  84. type Breed
    = Sheltie
    | Corgi
    | GoldenRetriever
    | Mix Breed Breed
    dog1 = Dog "Tucker" 11 Sheltie
    dog2 = Dog "Sally" 5 Corgi
    dog3 = Dog "Rover" 1 Mix

    View full-size slide

  85. type Breed
    = Sheltie
    | Corgi
    | GoldenRetriever
    | Mix Breed Breed
    dog1 = Dog "Tucker" 11 Sheltie
    dog2 = Dog "Sally" 5 Corgi
    dog3 = Dog "Rover" 1 Mix

    View full-size slide

  86. type Breed
    = Sheltie
    | Corgi
    | GoldenRetriever
    | Mix Breed Breed
    dog1 = Dog "Tucker" 11 Sheltie
    dog2 = Dog "Sally" 5 Corgi
    dog3 = Dog "Rover" 1 Mix
    ?

    View full-size slide

  87. dog1 = Dog "Tucker" 11 Sheltie
    dog2 = Dog "Sally" 5 Corgi
    dog3 = Dog "Rover" 1 (Mix Sheltie Corgi)
    type Breed
    = Sheltie
    | Corgi
    | GoldenRetriever
    | Mix Breed Breed

    View full-size slide

  88. type Maybe a
    = Just a
    | Nothing
    safety

    View full-size slide

  89. type Maybe a
    = Just a
    | Nothing
    safety

    View full-size slide

  90. divide : number -> number -> Maybe Float
    divide x y =
    if y == 0 then
    Nothing
    else
    Just (x / y)
    divide 4 2 -- Just 2
    divide 4 0 -- Nothing

    View full-size slide

  91. divide : number -> number -> Maybe Float
    divide x y =
    if y == 0 then
    Nothing
    else
    Just (x / y)
    divide 4 2 -- Just 2
    divide 4 0 -- Nothing

    View full-size slide

  92. divide : number -> number -> Maybe Float
    divide x y =
    if y == 0 then
    Nothing
    else
    Just (x / y)
    divide 4 2 -- Just 2
    divide 4 0 -- Nothing

    View full-size slide

  93. divide : number -> number -> Maybe Float
    divide x y =
    if y == 0 then
    Nothing
    else
    Just (x / y)
    divide 4 2 -- Just 2
    divide 4 0 -- Nothing

    View full-size slide

  94. divide : number -> number -> Maybe Float
    divide x y =
    if y == 0 then
    Nothing
    else
    Just (x / y)
    divide 4 2 -- Just 2
    divide 4 0 -- Nothing

    View full-size slide

  95. case divide 4 2 of
    Just n ->
    "Result is " ++ (toString n)
    Nothing ->
    "No Result"

    View full-size slide

  96. case divide 4 2 of
    Just n ->
    "Result is " ++ (toString n)
    Nothing ->
    "No Result"

    View full-size slide

  97. case divide 4 2 of
    Just n ->
    "Result is " ++ (toString n)
    Nothing ->
    "No Result"

    View full-size slide

  98. case divide 4 2 of
    Just n ->
    "Result is " ++ (toString n)
    Nothing ->
    "No Result"

    View full-size slide

  99. case half 4 of
    Just n ->
    "Result is " ++ (toString n)

    View full-size slide

  100. Compiler
    REPL
    Dev Server
    built-in
    tooling
    Packages

    View full-size slide

  101. built-in
    framework
    elm
    +
    vs.

    View full-size slide

  102. architecture
    model-view-update
    unidirectional
    organized communication

    View full-size slide

  103. elm
    app
    model
    Commands
    HTTP
    Dates
    Random #’s

    View full-size slide

  104. elm
    app
    model
    Commands
    HTTP
    Dates
    Random #’s
    Subscriptions
    WebSockets
    Browser Window
    Mouse Position

    View full-size slide

  105. elm
    app
    model
    Commands
    HTTP
    Dates
    Random #’s
    Subscriptions
    WebSockets
    Browser Window
    Mouse Position
    Events
    Text Input
    Mouse Click

    View full-size slide

  106. elm
    app
    model
    Commands
    HTTP
    Dates
    Random #’s
    Subscriptions
    WebSockets
    Browser Window
    Mouse Position
    Events
    Text Input
    Mouse Click Tasks

    View full-size slide

  107. elm
    app
    model
    Commands
    HTTP
    Dates
    Random #’s
    Subscriptions
    WebSockets
    Browser Window
    Mouse Position
    Events
    Text Input
    Mouse Click Tasks

    View full-size slide

  108. elm
    app
    model
    Update

    View full-size slide

  109. elm
    app
    model
    Update
    View

    View full-size slide

  110. model
    Update View
    elm

    View full-size slide

  111. model
    Update View
    elm

    View full-size slide

  112. model
    Update View
    elm

    View full-size slide

  113. model
    Update View
    elm
    !

    View full-size slide

  114. model
    Update View
    elm
    !

    View full-size slide

  115. model
    Update View
    elm

    View full-size slide

  116. model
    Update View
    elm

    View full-size slide

  117. model
    Update View
    elm

    View full-size slide

  118. model
    Update View
    elm

    View full-size slide

  119. github.com/jfairbank/arch-elm
    Demo

    View full-size slide

  120. THANKS!
    Jeremy Fairbank
    blog.jeremyfairbank.com
    tw: @elpapapollo / gh: jfairbank
    |> elm-lang.org
    |> guide.elm-lang.org
    |> github.com/jfairbank/arch-elm

    View full-size slide