Elm Conf 2017: Solving the Boolean Identity Crisis

Elm Conf 2017: Solving the Boolean Identity Crisis

94bd558238b69c45d3d3e15797ae94f7?s=128

Jeremy Fairbank

September 28, 2017
Tweet

Transcript

  1. Jeremy Fairbank @elpapapollo / jfairbank

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

    hi@testdouble.com
  3. + Follow @elpapapollo for future updates

  4. None
  5. { ? }

  6. { dog: null, }

  7. FETCH?

  8. { fetching: true, dog: null, }

  9. SUCCESS?

  10. { fetching: false, success: true, dog: { name: 'Tucker' },

    }
  11. ERRORS?

  12. { fetching: false, success: false, dog: null, error: true, errorMessage:

    'Ruh roh!', }
  13. { fetching: true, success: true, dog: { name: 'Tucker' },

    error: true, errorMessage: 'Uh oh!', } ¯\_(ツ)_/¯ Invalid State
  14. { fetching: true, success: true, dog: { name: 'Tucker' },

    error: true, errorMessage: 'Uh oh!', } ¯\_(ツ)_/¯ Invalid State
  15. if (props.error) { ... } else if (props.fetching) { ...

    } else if (props.success) { ... } else { ... }
  16. STATE fetching success error <ready>

  17. PRIMITIVE OBSESSION USING COMIC SANS IS KINDA LIKE PRIMITIVE OBSESSION

  18. The Problem with Booleans…

  19. – Robert Harper “There is no information carried by a

    Boolean beyond its value, and that’s the rub.”
  20. binary data type with no inherent meaning boolean

  21. bookFlight "St. Louis" True ?

  22. ? bookFlight "St. Louis" True False True ? ?

  23. ?

  24. Boolean Algebra George Boole x ∧ y x ∨ y

    x ∨ (y ∧ z) ¬x
  25. Boolean Values True && True True || False True ||

    (True && False) not True
  26. Propositional Logic

  27. Premise 1: If it’s raining then it’s cloudy. Premise 2:

    It’s raining. Conclusion: It’s cloudy. Propositional Logic
  28. Premise 1: If it’s raining then it’s cloudy. Premise 2:

    It’s raining. Conclusion: It’s cloudy. PROPOSITIONS
  29. prop·o·si·tion a statement that expresses a concept that can be

    true or false
  30. Boolean ≠ Proposition A proposition, p, that is true is

    not the same as saying p is equal to true.
  31. LOSS OF INTENT

  32. LOSS OF INFORMATION

  33. None
  34. bookFlight "St. Louis" True ?

  35. bookFlight city isPremiumCustomer = if isPremiumCustomer then ... else ...

  36. bookFlight city isPremiumCustomer = if isPremiumCustomer then ... else ...

  37. bookFlight city isPremiumCustomer = if isPremiumCustomer then ... else ...

    Regular customer?
  38. viewOpacitySlider = viewSlider True viewVolumeSlider = viewSlider False view model

    = div [] [ viewOpacitySlider , viewVolumeSlider ] ?
  39. viewSlider msg isHorizontal = if isHorizontal then ... else ...

  40. viewSlider msg isHorizontal = if isHorizontal then ... else ...

    Implicit vertical slider
  41. – Uncle Bob (Robert Martin) “Boolean arguments loudly declare that

    the function does more than one thing. They are confusing and should be eliminated.”
  42. rotateFuelRod fuelRod 30 True rotateControlRod ?

  43. rotateFuelRod fuelRod 30 True rotateControlRod controlRod 30 True

  44. None
  45. rotateFuelRod fuelRod amount inDegrees = ... rotateControlRod controlRod amount inRadians

    = ...
  46. rotateFuelRod fuelRod amount inDegrees = ... rotateControlRod controlRod amount inRadians

    = ...
  47. rotateFuelRod fuelRod amount inDegrees = ... rotateControlRod controlRod amount inRadians

    = ...
  48. ? bookFlight "St. Louis" True False True ? ?

  49. bookFlight city isPremiumCustomer hasCheckedLuggage preferWindow = let luggageCost = if

    hasCheckedLuggage then ... else ... in if isPremiumCustomer then if hasCheckedLuggage then ... else ... else if hasCheckedLuggage then ... else ...
  50. None
  51. – Martin Fowler “…an API should be written to make

    it easier for the caller, so if we know where the caller is coming from we should design the API with that information in mind.”
  52. Write code for humans Not computers

  53. None
  54. bookFlight "St. Louis" "Premium"

  55. bookFlight city customerType = case customerType of "Premium" -> ...

    "Regular" -> ... _ -> ...
  56. bookFlight city customerType = case customerType of "Premium" -> ...

    "Regular" -> ... _ -> ...
  57. bookFlight "St. Louis" "premium" bookFlight "St. Louis" "economical" bookFlight "St.

    Louis" "" Primitive Obsession Revisited
  58. How do we represent a finite domain in Elm?

  59. type CustomerType = Premium | Regular bookFlight "St. Louis" Premium

    bookFlight "St. Louis" Regular
  60. bookFlight city customerType = case customerType of Premium -> ...

    Regular -> ...
  61. type Angle = Degrees Float | Radians Float rotateFuelRod fuelRod

    (Degrees 30) rotateControlRod controlRod (Radians pi)
  62. rotateFuelRod fuelRod angle = case angle of Degrees amount ->

    ... Radians amount -> ... rotateControlRod controlRod angle = case angle of Degrees amount -> ... Radians amount -> ...
  63. rotate rod angle = case angle of Degrees amount ->

    ... Radians amount -> ...
  64. rotate rod angle = case angle of Degrees amount ->

    ... Radians amount -> ...
  65. rotateInDegrees fuelRod 30 rotateInRadians controlRod pi

  66. rotateInDegrees fuelRod 30 rotateInRadians controlRod pi Still encourages primitive obsession

  67. rotate rod angle = case angle of Degrees amount ->

    rotateInDegrees rod amount Radians amount -> rotateInRadians rod amount
  68. Make APIs UNDERSTANDABLE and CONVENIENT

  69. Function True False

  70. None
  71. time = if doYouKnowTheTime person then tellMeTheTime person else """

    Does anybody really know what time it is? """
  72. time = if doYouKnowTheTime person then tellMeTheTime person else """

    Does anybody really know what time it is? """
  73. time = if doYouKnowTheTime person then tellMeTheTime person else """

    Does anybody really know what time it is? """
  74. time = if doYouKnowTheTime person then tellMeTheTime person else """

    Does anybody really know what time it is? """
  75. time = if doYouKnowTheTime person then tellMeTheTime person else tellMeTheTime

    person
  76. Boolean Blindness

  77. – Conor McBride “To make use of a Boolean you

    have to know its provenance so that you can know what it means.”
  78. Get your organic, homegrown booleans from Rhode Island! Providence, RI

  79. PROVENANCE

  80. function findDogByName(name, dogs) { if (name in dogs) { return

    `${name} is a ${dogs[name].breed}`; } else { return 'Heck! No pupper found.'; } }
  81. findDogByName name dogs = if Dict.member name dogs then case

    Dict.get name dogs of Just dog -> name ++ " is a " ++ dog.breed Nothing -> "Heck! No pupper found." else "Heck! No pupper found."
  82. findDogByName name dogs = if Dict.member name dogs then case

    Dict.get name dogs of Just dog -> name ++ " is a " ++ dog.breed Nothing -> "Heck! No pupper found." else "Heck! No pupper found."
  83. findDogByName name dogs = if Dict.member name dogs then case

    Dict.get name dogs of Just dog -> name ++ " is a " ++ dog.breed Nothing -> "Heck! No pupper found." else "Heck! No pupper found."
  84. canDivide numerator denominator = denominator /= 0 divisionResult numerator denominator

    = if canDivide numerator denominator then "The result is " ++ toString (numerator / denominator) else "Could not divide"
  85. canDivide numerator denominator = denominator /= 0 divisionResult numerator denominator

    = if canDivide numerator denominator then "The result is " ++ toString (numerator / denominator) else "Could not divide"
  86. divisionResult numerator denominator = if canDivide numerator denominator then "The

    result is " ++ toString (numerator / denominator) else "The result is " ++ toString (numerator / denominator)
  87. – Dan Licata “Boolean tests let you look, options let

    you see.”
  88. Alternative Return Values

  89. findDogByName name dogs = case Dict.get name dogs of Just

    dog -> name ++ " is a " ++ dog.breed Nothing -> "Heck! No pupper found."
  90. case whatTimeIsIt person of Just time -> ... Nothing ->

    ...
  91. case whatTimeIsIt person of Just time -> ... Nothing ->

    ...
  92. type TellTime = Time String | NoWatch | NoPhone |

    NoSundial case whatTimeIsIt person of Time time -> ... NoWatch -> ... NoPhone -> ... NoSundial -> ...
  93. divide numerator denominator = case denominator of 0 -> Err

    "Divide by zero" _ -> Ok (numerator / denominator) divisionResult numerator denominator = case divide 4 2 of Ok result -> "The result is " ++ toString result Err error -> "Could not divide: " ++ error
  94. See with Union Types

  95. { fetching = False , success = True , dog

    = Just { name = "Tucker" } , error = False , errorMessage = "" } Back to State
  96. if model.error then ... else if model.fetching then ... else

    if model.success then ... else ...
  97. if model.fetching then ... else if model.success then ... else

    ...
  98. { fetching = True , success = True , dog

    = { name = "Tucker" } , error = True , errorMessage = "Uh oh!" }
  99. https://youtu.be/IcgmSRJHu_8

  100. STATE fetching success error <ready>

  101. type RemoteDoggo = Ready | Fetching | Success Dog |

    Error String type alias Model = { dog : RemoteDoggo , ... }
  102. type RemoteDoggo = Ready | Fetching | Success Dog |

    Error String type alias Model = { dog : RemoteDoggo , ... }
  103. type RemoteDoggo = Ready | Fetching | Success Dog |

    Error String type alias Model = { dog : RemoteDoggo , ... }
  104. type RemoteDoggo = Ready | Fetching | Success Dog |

    Error String type alias Model = { dog : RemoteDoggo , ... }
  105. { dog = Ready, ... } { dog = Fetching,

    ... } { dog = Success { name = "Tucker" }, ... } { dog = Error "Uh oh!", ... }
  106. view : Model -> Html Msg view model = case

    model.dog of Ready -> viewFindDog model Fetching -> viewSpinner Success dog -> viewDog dog Error error -> viewError error
  107. view : Model -> Html Msg view model = case

    model.dog of Ready -> viewFindDog model Fetching -> viewSpinner Success dog -> viewDog dog -- Error error -> viewError error
  108. This `case` does not have branches for all possibilities. 47|>

    case model.dog of 48|> Ready -> viewFindDog model 49|> 50|> Fetching -> viewSpinner 51|> 52|> Success dog -> viewDog dog You need to account for the following values: Main.Error _ Add a branch to cover this pattern!
  109. package.elm-lang.org/packages/krisajenkins/remotedata/latest type RemoteData e a = NotAsked | Loading |

    Failure e | Success a
  110. package.elm-lang.org/packages/krisajenkins/remotedata/latest type RemoteData e a = NotAsked | Loading |

    Failure e | Success a
  111. package.elm-lang.org/packages/krisajenkins/remotedata/latest type RemoteData e a = NotAsked | Loading |

    Failure e | Success a
  112. type alias Todo = { isComplete : Bool , isEditing

    : Bool } type alias Customer = { isPlatinum : Bool , isGold : Bool , isSilver : Bool }
  113. type TodoStatus = Complete | Editing type alias Todo =

    { status : TodoStatus }
  114. type AccountType = Platinum | Gold | Silver type alias

    Customer = { accountType : AccountType }
  115. UT Impossible state becomes impossible* Code becomes understandable *trademark Richard

    Feldman
  116. But wait, There’s MORE! AS SEEN ON U I

  117. Save to archive

  118. Save to archive A wild checkbox appears!

  119. Save to archive ✓

  120. Save to archive ?

  121. None
  122. We design APIs for callers So we should design UIs

    for users
  123. None
  124. hu·mane having or showing compassion or benevolence

  125. Save to archive ?

  126. Save to archive Save to inbox

  127. Save to archive Save to inbox

  128. Save to archive Save to inbox Verb labels = when

    will it happen?
  129. Saved to archive Saved to inbox Adjective labels = describe

    the final result.
  130. Goodbye, boolean?

  131. B A L A N C E

  132. EMPATHY

  133. Jeremy Fairbank @elpapapollo / jfairbank Slides: bit.ly/elm-bool