Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

14. Units of measure

A plain float cannot tell you whether it holds metres, seconds, or kilograms, and mixing them up is a classic source of real bugs. Pyfun lets you tag a number with a unit and then checks the units the way it checks types. You declare a base unit with measure, and a literal carries that unit when the annotation touches the digits, like 100.0<m>.

measure m
measure s
measure kg

measure N = kg m / s^2                # newton, a derived alias

let distance = 100.0<m>
let elapsed = 10.0<s>
let speed = distance / elapsed        # float<m/s>
let side = sqrt 16.0<m^2>             # float<m>
let force = 6.0<N>                    # a value in newtons

print speed
print side
print force
10.0
4.0
6.0

Arithmetic combines units the way dimensional analysis does. Dividing float<m> by float<s> derives float<m/s>, so speed carries the right unit without you writing it down. sqrt halves the exponents, taking a float<m^2> to a float<m>, which is the length of a square’s side from its area. A measure N = kg m / s^2 names a compound of base units, so 6.0<N> is a newton and the compiler knows it means the same thing as 6.0<kg m / s^2>.

Adding quantities with different units is where the checker steps in. Writing distance + elapsed is rejected before any Python is produced:

error: type mismatch: expected float<m>, found float<s>

The units exist only during type checking. They erase at lowering, so the emitted Python is plain numbers with no unit machinery to slow it down:

import math
distance = 100.0
elapsed = 10.0
speed = distance / elapsed
side = math.sqrt(16.0)

Exercise

The program below wants the runner’s speed, but it adds a distance to a time, which does not type-check. Run pyfun check to see the mismatch, then change the one operator so the units line up and the value becomes a speed in float<m/s>. Speed is distance divided by time.

measure m
measure s

let distance = 240.0<m>
let elapsed = 30.0<s>

# Speed is distance per unit of time. This line does not type-check yet.
let speed = distance + elapsed

print speed

The checker reports:

error: type mismatch: expected float<m>, found float<s>
 --> 8:13
  |
8 | let speed = distance + elapsed
  |             ^^^^^^^^^^^^^^^^^^

Expected output:

8.0

Open in the playground

Show solution
measure m
measure s

let distance = 240.0<m>
let elapsed = 30.0<s>

# Speed is distance per unit of time.
let speed = distance / elapsed

print speed

Dividing float<m> by float<s> derives float<m/s>, so the units agree and 240.0 / 30.0 prints 8.0.