Fluent Conf 2017: Tame the frontend with Elm

Fluent Conf 2017: Tame the frontend with Elm

94bd558238b69c45d3d3e15797ae94f7?s=128

Jeremy Fairbank

June 21, 2017
Tweet

Transcript

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

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

    hello@testdouble.com
  3. Happiness

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

    ✓ Easier to refactor
  6. elm

  7. No runtime exceptions in practice.

  8. No undefined is not a function

  9. Fast

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

  11. elm

  12. Functional

  13. greet name = "Hello, " ++ name greet "Fluent Conf"

    -- Hello, Fluent Conf
  14. greet name = "Hello, " ++ name greet "Fluent Conf"

    -- Hello, Fluent Conf
  15. greet name = "Hello, " ++ name greet "Fluent Conf"

    -- Hello, Fluent Conf
  16. greet name = "Hello, " ++ name greet "Fluent Conf"

    -- Hello, Fluent Conf Single Expression
  17. greet name = "Hello, " ++ name greet "Fluent Conf"

    -- Hello, Fluent Conf
  18. greet name = "Hello, " ++ name greet "Fluent Conf"

    -- Hello, Fluent Conf
  19. Pure Data in Data out

  20. Pure No side effects

  21. Pure Predictable and Testable!

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

    == 5 add 2 3 == 5 add 2 3 == 5
  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. Expressive Terse and declarative code

  26. myList = [1, 2, 3, 4, 5] double n =

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

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

    n * 2 doubleNumbers list = List.map double list doubleNumbers myList -- [2, 4, 6, 8, 10]
  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. Curried Functions Building blocks

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

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

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

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

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

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

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

    -- 42
  40. Pipes Expressive Chaining

  41. exclaim (greet (String.toUpper "Tucker")) greet name = "Hello, " ++

    name exclaim phrase = phrase ++ "!"
  42. "Tucker" |> String.toUpper |> greet |> exclaim

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

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

  45. "TUCKER" |> greet |> exclaim

  46. |> greet "TUCKER" |> exclaim

  47. "Hello, TUCKER" |> exclaim

  48. |> exclaim "Hello, TUCKER"

  49. "Hello, TUCKER!"

  50. No Runtime Exceptions

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

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

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

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

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

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

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

    ++ name add : Int -> (Int -> Int) add x y = x + y
  58. The argument to function `greet` is causing a mismatch. 11|

    greet 42 ^^ Function `greet` is expecting the argument to be: String But it is: number Compile time static type checks
  59. Immutable Data Safe and Consistent

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

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

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

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

    dog = { name = "Tucker" , age = 11 } dog.name -- "Tucker" dog.age -- 11 Records
  64. 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
  65. 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
  66. 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
  67. 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
  68. 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
  69. 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
  70. Custom Types Domain-specific code

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

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

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

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

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

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

    age = 11 , breed = Sheltie }
  77. No null or undefined

  78. type Maybe a = Just a | Nothing

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

    successful value
  80. type Maybe a = Just a | Nothing Represents no

    result or missing value
  81. type Maybe a = Just a | Nothing Type Variable

  82. type Maybe a = Just a | Nothing Either I

    have Just the value a, or I have Nothing.
  83. 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
  84. 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
  85. 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
  86. 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
  87. 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
  88. 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
  89. case divide 4 2 of Just n -> "Result is

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

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

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

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

    " ++ (toString n) -- Nothing -> -- "No Result"
  94. 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!
  95. Update View Model Messages The Elm Architecture

  96. Demos The Elm Architecture in Action

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

    • builtwithelm.co • Slack • elmlang.herokuapp.com
  98. Say hello@testdouble.com Thank you! Jeremy Fairbank / @elpapapollo