Skip to content
AgileLogics
5 min read

Units of Measure

Prerequisites: Numbers and math

Units of measure attach units to numbers and have the compiler check them — like the physics rule that you can't add metres to seconds.

[<Measure>] type m
[<Measure>] type s
[<Measure>] type kg

let distance = 100.0<m>
let time = 9.58<s>
let speed = distance / time

printfn "%.2f m/s" (float speed)

let mass = 80.0<kg>
let momentum = mass * speed
printfn "%.1f" (float momentum)
10.44 m/s
835.1

The compiler infers speed as float<m/s> and momentum as float<kg m/s>. Units exist only at compile time — at run time they're plain floats, so there's no performance cost.

Catching unit mistakes

[<Measure>] type m
[<Measure>] type s

let total = 5.0<m> + 3.0<s>
main.fsx(4,22): error FS0001: The unit of measure 's' does not match the unit of measure 'm'

This is exactly the kind of error that has destroyed real spacecraft.

Conversions

[<Measure>] type m
[<Measure>] type km
[<Measure>] type mile

let metresPerKm = 1000.0<m/km>
let kmPerMile = 1.609344<km/mile>

let marathon = 26.2188<mile>
let inKm = marathon * kmPerMile
let inMetres = inKm * metresPerKm

printfn "%.3f km" (float inKm)
printfn "%.0f m" (float inMetres)

let toKm (x: float<m>) = x / metresPerKm
printfn "%.1f km" (float (toKm 5000.0<m>))
42.195 km
42195 m
5.0 km

Conversion factors are just values with a compound unit like m/km. Multiplying cancels units the same way it does on paper.

Money, too

Units work on decimal and int as well, which is useful for keeping currencies apart:

[<Measure>] type EUR
[<Measure>] type USD

let price = 20m<EUR>
let rate = 1.08m<USD/EUR>
let inDollars = price * rate

printfn "%M USD" (decimal inDollars)

let addPrices (a: decimal<EUR>) (b: decimal<EUR>) = a + b
printfn "%M EUR" (decimal (addPrices 20m<EUR> 5m<EUR>))
21.60 USD
25 EUR
  • Declare units with [<Measure>] type name.
  • Write values as 1.5<m>; strip the unit with float x or decimal x.
  • Add a unit to a plain number by multiplying: x * 1.0<m>.
  • The standard SI units are predefined in FSharp.Data.UnitSystems.SI.UnitNames (meter, second, ...).

What's next

Try the Intermediate Projects, then continue to Level 3 — Advanced: computation expressions, async programming, testing, web development, and taking F# to production.