Pro Yearly is on sale from $80 to $50! »

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

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

94bd558238b69c45d3d3e15797ae94f7?s=128

Jeremy Fairbank

January 12, 2017
Tweet

Transcript

  1. TOWARD A BETTER |> FRONT END |> ARCHITECTURE elm Jeremy

    Fairbank blog.jeremyfairbank.com tw: @elpapapollo / gh: jfairbank
  2. sigient.com Your website, SimplyBuilt. simplybuilt.com

  3. None
  4. None
  5. None
  6. × two-way data binding mvc

  7. × ✓ two-way data binding unidirectional mvc components

  8. None
  9. null undefined is not a function

  10. let name = 'Jeremy'; export function getName() { return name;

    } export function setName(newName) { name = newName; } Impure and Mutable
  11. Unsafe APIs axios.get(`/user/${id}`) .then((user) => { console.log(user.name); }) .catch((error) =>

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

    { console.log(error); });
  13. Error Handling function mayThrow() { throw new Error('Whoops...') } function

    unsafe() { try { mayThrow(); } catch (e) { handleError(e); } } Call Stack ×
  14. axios.get(`/user/${id}`) .then((user) => { console.log(user.name); }); Error Handling

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

  16. elm

  17. No runtime errors in practice. No null. No undefined is

    not a function. - guide.elm-lang.org
  18. confidence

  19. confidence Static typing Expressive Terse Pure Unidirectional Architecture Immutable

  20. functional

  21. greet name = "Hello, " ++ name add x y

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

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

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

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

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

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

  28. pure add x y = x + y add 2

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

    3 -- 5 add 2 3 -- 5 add 2 3 -- 5 Referentially Transparent
  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
  31. declarative Declare what the desired result is

  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
  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 ×
  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
  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
  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
  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
  38. currying Creating building blocks

  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
  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
  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
  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
  43. currying double n = n * 2 doubleNumbers list =

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

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

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

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

  48. list = List.range 1 10 square n = n *

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

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

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

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

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

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

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

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

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

    ((<) 6) mapped |> List.map square filtered squared = List.map square filtered
  58. strong static typing Creating and adhering to contracts

  59. life : Int life = 42 greeting : String greeting

    = "Hello World" isTrue : Bool isTrue = True numbers : List Int numbers = [1, 2, 3] strong static typing
  60. strong static typing greet : String -> String greet name

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

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

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

    = "Hello, " ++ name add : Int -> (Int -> Int) add x y = x + y
  64. greet True add 2 "5"

  65. dog : ( String, Int ) dog = ( "Tucker",

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

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

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

    11 ) name = Tuple.first dog -- "Tucker" age = Tuple.second dog -- 11 tuples
  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"
  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"
  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"
  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"
  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"
  74. type alias Dog = { name : String , age

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

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

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

    age : Int , breed : String } dog : Dog dog = Dog "Tucker" 11 "Sheltie"
  78. immutable Create state, don’t mutate it

  79. immutable dog = Dog "Tucker" 11 "Sheltie" olderDog = {

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

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

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

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

    dog | age = dog.age + 1 } dog.age -- 11 olderDog.age -- 12
  84. union types type alias Dog = { name : String

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

    , age : Int , breed : String }
  86. type Breed = Sheltie | Corgi | GoldenRetriever | Mix

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

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

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

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

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

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

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

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

    Breed Breed dog1 = Dog "Tucker" 11 Sheltie dog2 = Dog "Sally" 5 Corgi dog3 = Dog "Rover" 1 Mix ?
  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
  96. null

  97. type Maybe a = Just a | Nothing safety

  98. type Maybe a = Just a | Nothing safety

  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
  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
  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
  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
  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
  104. case divide 4 2 of Just n -> "Result is

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

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

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

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

    ++ (toString n)
  109. Compiler REPL Dev Server built-in tooling Packages

  110. built-in framework elm + vs.

  111. architecture model-view-update unidirectional organized communication

  112. elm app model

  113. elm app model

  114. elm app model

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

  116. elm app model Commands HTTP Dates Random #’s Subscriptions WebSockets

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

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

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

    Browser Window Mouse Position Events Text Input Mouse Click Tasks
  120. elm app

  121. elm app model

  122. elm app model Update

  123. elm app model Update View

  124. model Update View elm

  125. model Update View elm

  126. model Update View elm

  127. model Update View elm !

  128. model Update View elm !

  129. model Update View elm

  130. model Update View elm

  131. model Update View elm

  132. model Update View elm

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

  134. THANKS! Jeremy Fairbank blog.jeremyfairbank.com tw: @elpapapollo / gh: jfairbank |>

    elm-lang.org |> guide.elm-lang.org |> github.com/jfairbank/arch-elm