Joël Quenneville
April 26, 2019
140

# A Number by Any Other Name

In 1999, NASA's Mars Climate Orbiter crashed into the red planet as it was
trying to enter orbit. The problem? Two pieces of software were using different
units in their calculations.

If you've ever calculated a wrong value because you forgot to convert seconds
into hours or because you accidentally inverted two number arguments in a
function, then you've encountered this same bug.

Join me as we leverage Elm's type system to catch these bugs, get better
error messages, and improved readability in the process. Along the way we'll
examine some assumptions we have around numbers and the operations we can do on
them.

April 26, 2019

## Transcript

5. ### Root Cause: Failure to use metric units in the coding

of a trajectory model.
6. ### [...] the navigation software algorithm therefore, underestimated the effect on

the spacecraft trajectory by a factor of 4.45 [...]

10. ### 3 KINDS OF MISTAKES 1. Argument order 2. Unit conversion

3. Invalid operation

12. ### ARGUMENT ORDER ERROR speed : Float -> Float -> Float

speed distance time = distance / time
13. ### ARGUMENT ORDER ERROR distance = 10 time = 5 --

WHICH ONE? speed distance time speed time distance

22. ### COMPILER CAN'T TELL THE DIFFERENCE Float -> Float -> Float

-- SAME AS Meter -> Second -> MeterPerSecond

24. ### [the Elm compiler] has my back when I make a

dumb mistake. It brings joy into front-end programming. - Joël Quenneville

27. ### CUSTOM TYPES type Meter = Meter Float type Second =

Second Float type MetersPerSecond = MetersPerSecond Float

29. ### speed : Meter -> Second -> MetersPerSecond speed (Meter m)

(Second s) = MetersPerSecond (m / s)
30. ### ARGUMENT ORDER ERROR - TYPED distance = Meter 10 time

= Second 5 -- Wrong argument order myVal = speed time distance
31. ### The 1st argument to `speed` is not what I expect:

21| myVal = speed time distance ^^^^ This `time` value is a: Second But `speed` needs the 1st argument to be: Meter
32. ### 3 KINDS OF MISTAKES 1. Argument order 2. Unit conversion

3. Invalid operation

34. ### UNIT CONVERSION ERROR minutes = 40 hours = 2 total

= minutes + hours -- RETURNS 42 -- SHOULD RETURN 160 OR 2.67

???
38. ### COMBINATORIAL EXPLOSION add : Minute -> Hour -> ??? add

: Minute -> Minute -> ??? add : Hour -> Hour -> ???

Float
45. ### OPAQUE TYPES module Time exposing (Time, fromMinutes) -- OTHER STUFF

fromMinutes : Float -> Time fromMinutes mins = Time mins
46. ### OPAQUE TYPES module Time exposing (Time, fromMinutes, fromHours) -- OTHER

STUFF fromHours : Float -> Time fromHours hrs = Time (hrs * 60)
47. ### OPAQUE TYPES module Time exposing (Time, fromMinutes, fromHours, add) --

OTHER STUFF add : Time -> Time -> Time add (Time t1) (Time t2) = Time (t1 + t2)
48. ### OPAQUE TYPES module Time exposing (Time, fromMinutes, fromHours, add) --

OTHER STUFF inHours : Time -> Float inHours (Time t) toFloat t / 60
49. ### minutes = Time.fromMinutes 40 hours = Time.fromHours 2 total =

Time.add hours minutes -- RETURNS `Time 160`

54. ### MONEY usd = 5 eur = 10 total = usd

+ eur -- RETURNS 15 -- SHOULD BE ??

56. ### RULES 1. Can only add currencies of the same type

2. Can convert currencies of different types given a rate

58. ### module Usd exposing(Usd, fromCents, add, toEur) add : Usd ->

Usd -> Usd toEur : Float -> Usd -> Eur -- MORE CODE
59. ### module Eur exposing (Eur, fromCents, add, toUsd) add : Eur

-> Eur -> Eur toUsd : Float -> Eur -> Usd -- MORE CODE
60. ### SUCCESS? usd = Usd.fromCents 500 eur = Eur.fromCents 1000 total

= eur |> Eur.toUsd 1.14 |> Usd.add usd -- RETURNS `Usd 1640`

66. ### RULES 1. Can only add currencies of the same type

2. Can convert currencies of different types given a rate

68. ### add : Currency a -> Currency a -> Currency a

convert : ConversionRate from to -> Currency from -> Currency to

71. ### module Currency exposing (Currency, Usd, Eur, Jpy) type Usd =

Usd type Eur = Eur type Jpy = Jpy type Currency a = Currency Float
72. ### module Currency exposing (Currency, Usd, Eur, Jpy, add) -- OTHER

CODE add : Currency a -> Currency a -> Currency a add (Currency c1) (Currency c2) = Currency (c1 + c2)
73. ### usd : Currency Usd usd = Currency 500 eur :

Currency Eur eur = Currency 1000 total = Currency.add usd eur
74. ### The 2nd argument to `add` is not what I expect:

29| add usd eur ^^^ This `eur` value is a: Currency Eur But `add` needs the 2nd argument to be: Currency Usd

76. ### CONVERSION - PHANTOM TYPE X 2 type ConversionRate from to

= ConversionRate Float convert : ConversionRate from to -> Currency from -> Currency to convert (ConversionRate rate) (Currency c) = Currency (rate * c)
77. ### -- usd AND eur DEFINITIONS HIDDEN rate : ConversionRate Eur

Usd rate = ConversionRate 1.12 total = eur |> Currency.convert rate |> Currency.add usd
78. ### GOING ALL THE WAY addDifferent : Currency a -> ConversionRate

a b -> Currency b -> Currency b
79. ### 3 KINDS OF MISTAKES 1. Argument order 2. Unit conversion

3. Invalid operation

82. ### INVALID OPERATION total : Float -> Float -> Float total

subTotal tax = subTotal * tax
83. ### total : Dollar -> Dollar -> DollarSquared total subTotal tax

= subTotal |> Dollar.times tax

97. ### CUSTOM TYPES type alias Point = (Int, Int) type WorldSpace

= WorldSpace Point type ScreenSpace = ScreenSpace Point
98. ### CONVERTING BETWEEN THE TWO - VIEWPORT type alias Viewport =

{ position : WorldSpace , width : GameDistance , height : GameDistance }
99. ### renderCharacter : Viewport -> Character -> Svg a renderCharacter vp

character = character.position |> toScreenCoords vp |> renderSpriteAt "character.png"
100. ### 3 KINDS OF MISTAKES 1. Argument order 2. Unit conversion

3. Invalid operation