Jeremy Fairbank
January 10, 2020
130

# CodeMash 2020: Solving the Boolean Identity Crisis

While powerful in its simplicity and important to computation, the boolean can be limiting in applications. In this talk, briefly explore the history of boolean logic in computation and look at how booleans can become misused in programming languages. Explore examples where booleans obscure the meaning of code, make code harder to maintain, and hinder usability for teammates and users. Learn how to harness custom types and higher-order functions to write clearer code. More importantly, learn how to place empathy and usability at the forefront of the APIs and UIs you build.

January 10, 2020

## Transcript

}

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

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

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

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

} else if (props.success) { ... } else { ... }

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

Boolean beyond its value, and that’s the rub.”

the code.

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

x ∨ (y ∧ z) ¬x
25. ### Boolean Values true && true true || false true ||

(true && false) !true

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.

{ ... } }

{ ... } }
37. ### function bookFlight(airport, isPremium) { if (isPremium) { ... } else

{ ... } } Regular customer?
38. ### const OpacitySlider = createSlider(true) const VolumeSlider = createSlider(false) const App

= () => ( <div> <OpacitySlider /> <VolumeSlider /> </div> ) ?
39. ### const createSlider = isHorizontal => () => { if (isHorizontal)

{ ... } else { ... } }
40. ### const createSlider = isHorizontal => () => { if (isHorizontal)

{ ... } else { ... } } Implicit vertical slider
41. ### – Robert Martin “Boolean arguments loudly declare that the function

does more than one thing. They are confusing and should be eliminated.”

48. ### function bookFlight(airport, isPremium, hasCheckedLuggage, preferWindow) { const luggageCost = hasCheckedLuggage

? ... : ... if (isPremium) { if (hasCheckedLuggage) { ... } else { ... } } else if (hasCheckedLuggage) { ... } else { ... } }
49. ### – 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. ### function bookFlight(airport, customerType) { switch (customerType) { case 'Premium': ...

case 'Regular': ... default: ... } }
53. ### function bookFlight(airport, customerType) { switch (customerType) { case 'Premium': ...

case 'Regular': ... default: ... } }

58. ### bookFlight city customerType = case customerType of Premium -> ...

Regular -> ...
59. ### bookFlight city customerType = case customerType of Premium -> ...

Regular -> ...
60. ### bookFlight city customerType = case customerType of Premium -> ...

Regular -> ...

62. ### function bookFlight(airport, customerType) { switch (customerType) { case CustomerType.Premium: ...

case CustomerType.Regular: ... default: ... } }
63. ### function bookFlight(airport, customerType) { switch (customerType) { case CustomerType.Premium: ...

case CustomerType.Regular: ... default: ... } }

65. ### function bookFlight( airport: string, customerType: CustomerType, ): Promise<Flight> { switch

(customerType) { case CustomerType.Premium: ... case CustomerType.Regular: ... } }
66. ### function bookFlight( airport: string, customerType: CustomerType, ): Promise<Flight> { switch

(customerType) { case CustomerType.Premium: ... case CustomerType.Regular: ... } }
67. ### function bookFlight( airport: string, customerType: CustomerType, ): Promise<Flight> { switch

(customerType) { case CustomerType.Premium: ... case CustomerType.Regular: ... } }
68. ### const AngleType = { Degrees: 'Degrees', Radians: 'Radians', } const

degrees = value => ({ kind: AngleType.Degrees, value }) const radians = value => ({ kind: AngleType.Radians, value })
69. ### const AngleType = { Degrees: 'Degrees', Radians: 'Radians', } const

degrees = value => ({ kind: AngleType.Degrees, value }) const radians = value => ({ kind: AngleType.Radians, value })
70. ### const AngleType = { Degrees: 'Degrees', Radians: 'Radians', } const

degrees = value => ({ kind: AngleType.Degrees, value }) const radians = value => ({ kind: AngleType.Radians, value })

72. ### enum AngleType { Degrees = 'Degrees', Radians = 'Radians', }

type Angle = { kind: AngleType, value: number, }
73. ### enum AngleType { Degrees = 'Degrees', Radians = 'Radians', }

type Angle = { kind: AngleType, value: number, }
74. ### enum AngleType { Degrees = 'Degrees', Radians = 'Radians', }

type Angle = { kind: AngleType, value: number, }
75. ### function rotateFuelRod( fuelRod: FuelRod, angle: Angle, ): FuelRod { switch

(angle.kind) { case AngleType.Degrees: ... case AngleType.Radians: ... } }
76. ### function rotate( rod: Rod, angle: Angle, ): Rod { switch

(angle.kind) { case AngleType.Degrees: ... case AngleType.Radians: ... } }
77. ### function rotate( rod: Rod, angle: Angle, ): Rod { switch

(angle.kind) { case AngleType.Degrees: ... case AngleType.Radians: ... } }
78. ### function rotate( rod: Rod, angle: Angle, ): Rod { switch

(angle.kind) { case AngleType.Degrees: ... case AngleType.Radians: ... } }

81. ### function rotate( rod: Rod, angle: Angle, ): Rod { switch

(angle.kind) { case AngleType.Degrees: return rotateInDegrees(rod, angle.value) case AngleType.Radians: return rotateInRadians(rod, angle.value) } }

'foobar'], )
83. ### const matchingStrings = (case_, pattern, strings) => strings.filter(string => {

switch (case_) { case Case.CaseSensitive: return string.includes(pattern) case Case.CaseInsensitive: return string .toUpperCase() .includes(pattern.toUpperCase()) } })
84. ### const matchingStrings = (case_, pattern, strings) => strings.filter(string => {

switch (case_) { case Case.CaseSensitive: return string.includes(pattern) case Case.CaseInsensitive: return string .toUpperCase() .includes(pattern.toUpperCase()) } })
85. ### const matchingStrings = (case_, pattern, strings) => strings.filter(string => {

switch (case_) { case Case.CaseSensitive: return string.includes(pattern) case Case.CaseInsensitive: return string .toUpperCase() .includes(pattern.toUpperCase()) } })
86. ### const caseInsensitive = string => string.toUpperCase() const caseSensitive = string

=> string matchingStrings( caseInsensitive, 'foo', ['FOOBAR', 'foobar'], ) matchingStrings( caseSensitive, 'foo', ['FOOBAR', 'foobar'], )
87. ### const caseInsensitive = string => string.toUpperCase() const caseSensitive = string

=> string matchingStrings( caseInsensitive, 'foo', ['FOOBAR', 'foobar'], ) matchingStrings( caseSensitive, 'foo', ['FOOBAR', 'foobar'], )
88. ### const caseInsensitive = string => string.toUpperCase() const caseSensitive = string

=> string matchingStrings( caseInsensitive, 'foo', ['FOOBAR', 'foobar'], ) matchingStrings( caseSensitive, 'foo', ['FOOBAR', 'foobar'], )
89. ### const matchingStrings = ( normalize, pattern, strings ) => strings.filter(string

=> normalize(string).includes(normalize(pattern)) )
90. ### const matchingStrings = ( normalize, pattern, strings ) => strings.filter(string

=> normalize(string).includes(normalize(pattern)) )
91. ### const matchingStrings = ( normalize, pattern, strings ) => strings.filter(string

=> normalize(string).includes(normalize(pattern)) )
92. ### const matchingStrings = ( normalize, pattern, strings ) => strings.filter(string

=> normalize(string).includes(normalize(pattern)) ) Higher-order Function
93. ### const bookPremiumCustomer = () => { ... } const bookRegularCustomer

= () => { ... } bookFlight('CLE', bookPremiumCustomer) bookFlight('CLE', bookRegularCustomer)

96. ### function getTime(person) { if (doYouKnowTheTime(person)) { return tellMeTheTime(person) } else

{ return ` Does anybody really know what time it is? ` } }
97. ### function getTime(person) { if (doYouKnowTheTime(person)) { return tellMeTheTime(person) } else

{ return ` Does anybody really know what time it is? ` } }
98. ### function getTime(person) { if (doYouKnowTheTime(person)) { return tellMeTheTime(person) } else

{ return ` Does anybody really know what time it is? ` } }
99. ### function getTime(person) { if (doYouKnowTheTime(person)) { return tellMeTheTime(person) } else

{ return ` Does anybody really know what time it is? ` } }
100. ### function getTime(person) { if (doYouKnowTheTime(person)) { return tellMeTheTime(person) } else

{ return tellMeTheTime(person) } }

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

have to know its provenance so that you can know what it means.”

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

`\${name} is a \${dogs[name].breed}`; } else { return 'Heck! No pupper found.'; } }
106. ### function findDogByName(name, dogs) { if (name in dogs) { return

`\${name} is a \${dogs[name].breed}`; } else { return `\${name} is a \${dogs[name].breed}`; } }
107. ### function canDivide(denominator) { return denominator != 0 } function divisionResult(numerator,

denominator) { if (canDivide(denominator)) { return `The result is \${numerator / denominator}` } else { return 'Could not divide' } }
108. ### function canDivide(denominator) { return denominator != 0 } function divisionResult(numerator,

denominator) { if (canDivide(denominator)) { return `The result is \${numerator / denominator}` } else { return 'Could not divide' } }
109. ### function divisionResult(numerator, denominator) { if (canDivide(denominator)) { return `The result

is \${numerator / denominator}` } else { return `The result is \${numerator / denominator}` } }

you see.”

118. ### import { Maybe } from 'true-myth' Maybe.just(42) // Just 42

Maybe.nothing() // Nothing Maybe.of(42) // Just 42 Maybe.of(null) // Nothing Maybe.of(undefined) // Nothing
119. ### function findDogByName(name, dogs) { return Maybe.of(dogs[name]) .map(dog => `\${name} is

a \${dog.breed}.`) .unwrapOr('Heck! No pupper found!') }
120. ### function findDogByName(name, dogs) { return Maybe.of(dogs[name]) .map(dog => `\${name} is

a \${dog.breed}.`) .unwrapOr('Heck! No pupper found!') }

122. ### function findDogByName(name, dogs) { return Maybe.of(dogs[name]) .map(dog => `\${name} is

a \${dog.breed}.`) .unwrapOr('Heck! No pupper found!') }
123. ### .map n => n * 2 Just 21 .map n

=> n * 2 Just 42 Nothing Nothing
124. ### function findDogByName(name, dogs) { return Maybe.of(dogs[name]) .map(dog => `\${name} is

a \${dog.breed}.`) .unwrapOr('Heck! No pupper found!') }
125. ### function findDogByName(name, dogs) { return Maybe.of(dogs[name]) .map(dog => `\${name} is

a \${dog.breed}.`) .unwrapOr('Heck! No pupper found!') }

127. ### function findDogByName(name, dogs) { return Maybe.of(dogs[name]) .map(dog => `\${name} is

a \${dog.breed}.`) .unwrapOr('Heck! No pupper found!') }

132. ### import { Result } from 'true-myth' Result.ok(42) // Ok 42

Result.err('Uh oh') // Err 'Uh oh'
133. ### const divide = (numerator, denominator) => denominator === 0 ?

Result.err('Divide by zero') : Result.ok(numerator / denominator)
134. ### const divide = (numerator, denominator) => denominator === 0 ?

Result.err('Divide by zero') : Result.ok(numerator / denominator)
135. ### const divide = (numerator, denominator) => denominator === 0 ?

Result.err('Divide by zero') : Result.ok(numerator / denominator)
136. ### const divide = (numerator, denominator) => denominator === 0 ?

Result.err('Divide by zero') : Result.ok(numerator / denominator)
137. ### const divisionResult = (numerator, denominator) => divide(numerator, denominator) .mapOrElse( error

=> `Could not divide: \${error}`, quotient => `The quotient is \${quotient}`, )
138. ### const divisionResult = (numerator, denominator) => divide(numerator, denominator) .mapOrElse( error

=> `Could not divide: \${error}`, quotient => `The quotient is \${quotient}`, )
139. ### const divisionResult = (numerator, denominator) => divide(numerator, denominator) .mapOrElse( error

=> `Could not divide: \${error}`, quotient => `The quotient is \${quotient}`, )
140. ### const divisionResult = (numerator, denominator) => divide(numerator, denominator) .mapOrElse( error

=> `Could not divide: \${error}`, quotient => `The quotient is \${quotient}`, )

142. ### Back to State { fetching: false, success: true, dog: {

name: "Tucker" }, error: False, errorMessage: "", }
143. ### if (props.error) { ... } else if (props.fetching) { ...

} else if (props.success) { ... } else { ... }
144. ### { fetching: true, success: true, dog: { name: "Tucker" },

error: true, errorMessage: "Uh oh!", }

147. ### const RemoteDoggo = { Ready: 'Ready', Fetching: 'Fetching', Success: 'Success',

Fail: 'Fail', }
148. ### const ready = () => ({ status: RemoteDoggo.Ready }) const

fetching = () => ({ status: RemoteDoggo.Fetching }) const success = value => ({ status: RemoteDoggo.Success, value }) const fail = message => ({ status: RemoteDoggo.Fail, message })
149. ### const ready = () => ({ status: RemoteDoggo.Ready }) const

fetching = () => ({ status: RemoteDoggo.Fetching }) const success = value => ({ status: RemoteDoggo.Success, value }) const fail = message => ({ status: RemoteDoggo.Fail, message })
150. ### { dog: ready(), ... } { dog: fetching(), ... }

{ dog: success({ name: 'Tucker' }), ... } { dog: fail('Uh oh!'), ... }
151. ### function App({ dog }) { switch (dog.status) { case RemoteDoggo.Ready:

return <FindDog /> case RemoteDoggo.Fetching: return <Spinner /> case RemoteDoggo.Success: return <Dog dog={dog.value} /> case RemoteDoggo.Fail: return <Error message={dog.message} /> } }
152. ### function App({ dog }) { switch (dog.status) { case RemoteDoggo.Ready:

return <FindDog /> case RemoteDoggo.Fetching: return <Spinner /> case RemoteDoggo.Success: return <Dog dog={dog.value} /> case RemoteDoggo.Fail: return <Error message={dog.message} /> } }
153. ### enum Status { Ready = 'Ready', Fetching = 'Fetching', Success

= 'Success', Fail = 'Fail', } type RemoteDoggo = { kind: Status.Ready } | { kind: Status.Fetching } | { kind: Status.Success, value: Dog } | { kind: Status.Fail, message: string }
154. ### function App({ dog }: AppProps): React.ReactElement { switch (dog.kind) {

case Status.Ready: return <FindDog /> case Status.Fetching: return <Spinner /> case Status.Success: return <Dog dog={dog.value} /> // case Status.Fail: // return <Error message={dog.message} /> } }
155. ### app.tsx:20:34 - error TS2366: Function lacks ending return statement and

return type does not include 'undefined'. 20 function App({ dog }: AppProps): React.ReactElement { ~~~~~~~~~~~~~~~~~~ Found 1 error.

for users

167. ### Save to archive Save to inbox Verb labels = when

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

the final result.