Scenic City Summit 2017: Tame the frontend with Elm

Scenic City Summit 2017: Tame the frontend with Elm

94bd558238b69c45d3d3e15797ae94f7?s=128

Jeremy Fairbank

July 28, 2017
Tweet

Transcript

  1. Tame the frontend with Elm Jeremy Fairbank @elpapapollo / jfairbank

  2. Tame the frontend with Elm Jeremy Fairbank @elpapapollo / jfairbank

  3. Software is broken. We are here to fix it. Say

    hi@testdouble.com
  4. Happiness

  5. None
  6. ✓ Easier to write code ✓ Easier to write tests

    ✓ Easier to refactor
  7. elm

  8. No runtime exceptions in practice.

  9. No undefined is not a function

  10. Fast

  11. One framework. No fatigue. Update View Model Messages

  12. elm

  13. Functional

  14. greet name = "Hello, " ++ name greet "Scenic City

    Summit" -- Hello, Scenic City Summit
  15. greet name = "Hello, " ++ name greet "Scenic City

    Summit" -- Hello, Scenic City Summit
  16. greet name = "Hello, " ++ name greet "Scenic City

    Summit" -- Hello, Scenic City Summit
  17. greet name = "Hello, " ++ name greet "Scenic City

    Summit" -- Hello, Scenic City Summit Single Expression
  18. greet name = "Hello, " ++ name greet "Scenic City

    Summit" -- Hello, Scenic City Summit
  19. greet name = "Hello, " ++ name greet "Scenic City

    Summit" -- Hello, Scenic City Summit
  20. Pure Data in Data out

  21. Pure No side effects

  22. Pure Predictable and Testable!

  23. add x y = x + y add 2 3

    == 5 add 2 3 == 5 add 2 3 == 5
  24. add x y = x + y add 2 3

    == 5 add 2 3 == 5 add 2 3 == 5
  25. add x y = x + y add 2 3

    == 5 add 2 3 == 5 add 2 3 == 5
  26. Expressive Terse and declarative code

  27. 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
  28. 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 ×
  29. myList = [1, 2, 3, 4, 5] double n =

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

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

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

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

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

    n * 2 doubleNumbers list = List.map double list doubleNumbers myList -- [2, 4, 6, 8, 10]
  35. Curried Functions Building blocks

  36. add x y = x + y add 1 2

    -- 3 (add 1) 2 -- 3
  37. add x y = x + y add 1 2

    -- 3 (add 1) 2 -- 3
  38. add x y = x + y add 1 2

    -- 3 (add 1) 2 -- 3
  39. add x y = x + y add 1 2

    -- 3 (add 1) 2 -- 3 New function created
  40. increment = add 1 increment 2 -- 3 increment 41

    -- 42
  41. increment = add 1 increment 2 -- 3 increment 41

    -- 42
  42. increment = add 1 increment 2 -- 3 increment 41

    -- 42
  43. Pipes Compose functions with expressive chaining

  44. greet name = "Hello, " ++ name exclaim phrase =

    phrase ++ "!" excitedGreeting name = exclaim (greet (String.toUpper name))
  45. greet name = "Hello, " ++ name exclaim phrase =

    phrase ++ "!" excitedGreeting name = exclaim (greet (String.toUpper name))
  46. excitedGreeting name = name |> String.toUpper |> greet |> exclaim

    excitedGreeting "Tucker"
  47. excitedGreeting name = name |> String.toUpper |> greet |> exclaim

    excitedGreeting "Tucker"
  48. "Tucker" |> String.toUpper |> greet |> exclaim

  49. |> String.toUpper "Tucker" |> greet |> exclaim

  50. "TUCKER" |> greet |> exclaim

  51. |> greet "TUCKER" |> exclaim

  52. "Hello, TUCKER" |> exclaim

  53. |> exclaim "Hello, TUCKER"

  54. "Hello, TUCKER!"

  55. No Runtime Exceptions

  56. Strong Static Types life : Int life = 42 greeting

    : String greeting = "Hello World" isTrue : Bool isTrue = True numbers : List Int numbers = [1, 2, 3]
  57. Strong Static Types life : Int life = 42 greeting

    : String greeting = "Hello World" isTrue : Bool isTrue = True numbers : List Int numbers = [1, 2, 3]
  58. Strong Static Types life : Int life = 42 greeting

    : String greeting = "Hello World" isTrue : Bool isTrue = True numbers : List Int numbers = [1, 2, 3]
  59. Strong Static Types life : Int life = 42 greeting

    : String greeting = "Hello World" isTrue : Bool isTrue = True numbers : List Int numbers = [1, 2, 3]
  60. Strong Static Types life : Int life = 42 greeting

    : String greeting = "Hello World" isTrue : Bool isTrue = True numbers : List Int numbers = [1, 2, 3]
  61. greet : String -> String greet name = "Hello, "

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

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

    ++ name add : Int -> Int -> Int add x y = x + y
  64. greet : String -> String greet name = "Hello, "

    ++ name add : Int -> (Int -> Int) add x y = x + y
  65. The 2nd argument to function `add` is causing a mismatch.

    7| add 2 "3" ^^^ Function `add` is expecting the 2nd argument to be: Int But it is: String Compile time static type checks
  66. Immutable Data Safe and Consistent

  67. dog : { name : String, age : Int }

    dog = { name = "Tucker" , age = 11 } dog.name -- "Tucker" dog.age -- 11 Records
  68. dog : { name : String, age : Int }

    dog = { name = "Tucker" , age = 11 } dog.name -- "Tucker" dog.age -- 11 Records
  69. dog : { name : String, age : Int }

    dog = { name = "Tucker" , age = 11 } dog.name -- "Tucker" dog.age -- 11 Records
  70. dog : { name : String, age : Int }

    dog = { name = "Tucker" , age = 11 } dog.name -- "Tucker" dog.age -- 11 Records
  71. haveBirthday dog = { dog | age = dog.age +

    1 } dog = { name = "Tucker", age = 11 } olderDog = haveBirthday dog olderDog.age -- 12 olderDog.name -- "Tucker" dog.age -- 11 dog.name -- "Tucker" Create New Data
  72. haveBirthday dog = { dog | age = dog.age +

    1 } dog = { name = "Tucker", age = 11 } olderDog = haveBirthday dog olderDog.age -- 12 olderDog.name -- "Tucker" dog.age -- 11 dog.name -- "Tucker" Create New Data
  73. haveBirthday dog = { dog | age = dog.age +

    1 } dog = { name = "Tucker", age = 11 } olderDog = haveBirthday dog olderDog.age -- 12 olderDog.name -- "Tucker" dog.age -- 11 dog.name -- "Tucker" Create New Data
  74. haveBirthday dog = { dog | age = dog.age +

    1 } dog = { name = "Tucker", age = 11 } olderDog = haveBirthday dog olderDog.age -- 12 olderDog.name -- "Tucker" dog.age -- 11 dog.name -- "Tucker" Create New Data
  75. haveBirthday dog = { dog | age = dog.age +

    1 } dog = { name = "Tucker", age = 11 } olderDog = haveBirthday dog olderDog.age -- 12 olderDog.name -- "Tucker" dog.age -- 11 dog.name -- "Tucker" Create New Data
  76. haveBirthday dog = { dog | age = dog.age +

    1 } dog = { name = "Tucker", age = 11 } olderDog = haveBirthday dog olderDog.age -- 12 olderDog.name -- "Tucker" dog.age -- 11 dog.name -- "Tucker" Create New Data
  77. Custom Types Domain-specific code

  78. type alias Dog = { name : String , age

    : Int , breed : Breed } type Breed = Sheltie | Poodle
  79. type alias Dog = { name : String , age

    : Int , breed : Breed } type Breed = Sheltie | Poodle
  80. type alias Dog = { name : String , age

    : Int , breed : Breed } type Breed = Sheltie | Poodle Union Type
  81. dog : Dog dog = { name = "Tucker" ,

    age = 11 , breed = Sheltie }
  82. dog : Dog dog = { name = "Tucker" ,

    age = 11 , breed = Sheltie }
  83. dog : Dog dog = { name = "Tucker" ,

    age = 11 , breed = Sheltie }
  84. dog : Dog dog = { name = "Tucker" ,

    age = 11 , breed = Shelty } Misspelled. Won’t compile!
  85. No null or undefined

  86. type Maybe a = Just a | Nothing

  87. type Maybe a = Just a | Nothing Wraps the

    successful value
  88. type Maybe a = Just a | Nothing Wraps the

    successful value Type Variable
  89. type Maybe a = Just a | Nothing Represents no

    result or missing value
  90. type Maybe a = Just a | Nothing Either I

    have Just the value a, or I have Nothing.
  91. divide : Float -> Float -> Maybe Float divide x

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

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

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

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

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

    y = if y == 0 then Nothing else Just (x / y) divide 4 2 -- Just 2 divide 4 0 -- Nothing
  97. case divide 4 2 of Just n -> "Result is

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

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

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

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

    " ++ (toString n) -- Nothing -> -- "No Result"
  102. Exhaustive matching This `case` does not have branches for all

    possibilities. 21|> case divide 4 2 of 22|> Just n -> 23|> "Result is " ++ (toString n) You need to account for the following values: Maybe.Nothing Add a branch to cover this pattern!
  103. Update View Model Messages The Elm Architecture

  104. model Update View

  105. model Update View VDOM

  106. model Update View

  107. model Update View

  108. model Update View

  109. model Update View

  110. model Update View

  111. model Update View VDOM

  112. Demos The Elm Architecture in Action

  113. Getting Started • elm-lang.org • elm-lang.org/examples • guide.elm-lang.org • www.elm-tutorial.org

    • builtwithelm.co • Slack • elmlang.herokuapp.com
  114. Thanks! Jeremy Fairbank @elpapapollo / jfairbank Slides: bit.ly/scs-elm