In 1999, the Mars Client Orbiter crashed putting a melancholic end to a $125-million project. The crash happened because two teams responsible for designing two software subsystems of the spacecraft used different units of measurement.
Although a silly bug, it’s not immediately clear how to avoid it in a Python program. Let’s stick to the unit of measurement problem but in a lower stake situation. Assume you want to calculate someone’s Body Mass Index (BMI).
The BMI formula is given by
$$BMI = \frac{weight}{height^2}$$
where weight and height are measured in kilograms and meters respectively.
def calculate_bmi(weight: float, height: float) -> float:
return weight / height**2
I am Brazilian and would probably not misuse calculate_bmi
, since I am used to the metric system. If I were born in the USA, however, I might have wanted to pass weight
in pounds and height
in feet.
# Me
calculate_bmi(82.4, 1.83) # > 24.605094210039116
# American me
calculate_bmi(181.7, 6.00) # > 5.047222222222222
One way to avoid this kind of bug is to use mypy with typing.NewType
.
# bmi.py
from typing import NewType
Kg = NewType('Kg', float)
Meter = NewType('Meter', float)
def calculate_bmi(weight: Kg, height: Meter) -> float:
return weight / height**2
print(calculate_bmi(82.4, 1.83))
bmi.py
doesn’t type-check. Notice I have changed the function annotation but I am still calling it with two floats (82.4
, 1.83
). Mypy – rightfully so – yells at me.
$ mypy bmi.py
Argument 1 to "calculate_bmi" has incompatible type "float"; expected "Kg"
Argument 2 to "calculate_bmi" has incompatible type "float"; expected "Meter"
To make mypy happy, you need to wrap the numbers in the newly created types Kg
and Meter
.
calculate_bmi(Kg(82.4), Meter(1.83))
This style of code is not very pythonic, but it does have its place. Specially,
when the same type (float
) can represent two different things (pounds and
kilos). At the very least, you get the correct value of your Body Mass
Index by requiring the caller of calculate_bmi
to explicitly declare the unit
of measurement of height
and weight
. In more critical scenarios, you might end up savingsaved NASA a couple million dollars.