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 Slide

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

    View Slide

  3. View Slide

  4. View Slide

  5. View Slide

  6. ×
    two-way
    data binding
    mvc

    View Slide

  7. ×

    two-way
    data binding
    unidirectional
    mvc
    components

    View Slide

  8. View Slide

  9. null
    undefined is not a function

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  16. elm

    View Slide

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

    View Slide

  18. confidence

    View Slide

  19. confidence
    Static typing
    Expressive
    Terse
    Pure
    Unidirectional
    Architecture
    Immutable

    View Slide

  20. functional

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  27. pure

    View Slide

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

    View Slide

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

    View Slide

  30. 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 Slide

  31. declarative
    Declare what the
    desired result is

    View Slide

  32. 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 Slide

  33. 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 Slide

  34. 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 Slide

  35. 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 Slide

  36. 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 Slide

  37. 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 Slide

  38. currying
    Creating building
    blocks

    View Slide

  39. 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 Slide

  40. 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 Slide

  41. 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 Slide

  42. 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 Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  58. strong static typing
    Creating and adhering
    to contracts

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  64. greet True
    add 2 "5"

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  69. 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 Slide

  70. 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 Slide

  71. 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 Slide

  72. 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 Slide

  73. 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 Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  78. immutable
    Create state, don’t
    mutate it

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  95. 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 Slide

  96. null

    View Slide

  97. type Maybe a
    = Just a
    | Nothing
    safety

    View Slide

  98. type Maybe a
    = Just a
    | Nothing
    safety

    View Slide

  99. 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 Slide

  100. 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 Slide

  101. 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 Slide

  102. 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 Slide

  103. 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 Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  109. Compiler
    REPL
    Dev Server
    built-in
    tooling
    Packages

    View Slide

  110. built-in
    framework
    elm
    +
    vs.

    View Slide

  111. architecture
    model-view-update
    unidirectional
    organized communication

    View Slide

  112. elm
    app
    model

    View Slide

  113. elm
    app
    model

    View Slide

  114. elm
    app
    model

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  120. elm
    app

    View Slide

  121. elm
    app
    model

    View Slide

  122. elm
    app
    model
    Update

    View Slide

  123. elm
    app
    model
    Update
    View

    View Slide

  124. model
    Update View
    elm

    View Slide

  125. model
    Update View
    elm

    View Slide

  126. model
    Update View
    elm

    View Slide

  127. model
    Update View
    elm
    !

    View Slide

  128. model
    Update View
    elm
    !

    View Slide

  129. model
    Update View
    elm

    View Slide

  130. model
    Update View
    elm

    View Slide

  131. model
    Update View
    elm

    View Slide

  132. model
    Update View
    elm

    View Slide

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

    View Slide

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

    View Slide