The Frink is Good, the Unit is Evil
Skip to Content
New: Logic for Programmers is now content-complete and in the hands of the copy editor! Get the current beta for 20% off here.
Hillel Wayne
Posted on<br>Jul 10, 2020
One day Alan Eliasen read a fart joke and got so mad he invented a programming language. 20 years later Frink is one of the best special purpose languages for dealing with units.
“But why do we need a language just for dealing with units?” Glad you asked!
Intro to Units
A unit is the physical property a number represents, like distance or time. We almost always are talking about SI units, or Système international. That’s things like meters, seconds, kilograms, along with prefixes like kilo- and mega-. One kilometer = 1000 meters, 1 centimeter = 1/100th of a meter.
Units can have exponents: m² is square meters, m³ is cubic meters, m * s-1 is speed. These form new physical properties, or dimensions . We can multiply and divide numbers with different dimensions but not add them. I can add 1 m + 1 m, but not 1 m + 1 kg. A unit-aware language needs to be able to track units and make sure that I don’t add incompatible dimensions. It also should also make sure that I pass correct units into functions that depend on units. A pressure calculation would take an area and a force, not a frequency and an amperage.
Simple, right?
Nope
First of all, there are multiple different representations of units. There’s the SI system, which all sane, right-thinking nations use, and then there’s the American system.1 Feet and meters are both distance. Is it okay to mix feet and meters? This is the bug that destroyed the Mars Climate Orbiter. But there are also valid uses cases where you’d want to mix them! When I cook, I use a mix of metric weights and American volumes, like 1 cup of water and 128 grams of flour. In the UK, beer is measured using pints but its alcohol by volume is measured in SI.
Different dimensions doesn’t always mean the units are incompatible. There’s a nonstandard set of units called “Gaussian units” used in some niches of physics. In SI, capacitance is measured in Farads, which has dimensions A² s⁴ kg-1 m−2. In Gaussian units, capacitance is measured in cm. If you’re explicit about what you’re doing you can add these seemingly-incompatible dimensions.
Dimensions aren’t unique, and two incompatible physical quantities can have the same dimension. The canonical example is that energy and angular force are both measured in Newton-meters.2 There are also plenty of domain specific examples. As you enter a gravity well, the rate at which gravitational force changes has dimensions N/m. Surface tension is also measured in N/m.
What else?
What’s 200° + 360°? It could be 200° or 560°, depending on we have a “circular angle” or a “rotational angle” (like driving a screw in).
Some quantities are unitless. What’s 20° + 1 radian? They’re both unitless quantities that have compatible dimensions. What about adding two ratios? What about adding a radian to a ratio?
Units can have additional limitations. You can subtract timestamps from each other but not add them.
Units can have different historical values. A foot is defined as exactly 0.3048 meters. Before 1959, though, it was 0.3048006 meters. This is means that all historical documents have a different measurement of foot. And there was a period of time where we used both definitions in different places!
Uncertainties. If you’re working with physical measurements, all of your measurements will have some amount of uncertainty. What happens when you add two meters, plus or minus a centimeter?
If you want to learn more about how evil units are, check out Bill Kent’s Measurement Data Report.
Some solutions
Adding units of measure to a language is tricky. It’s not just that there are many hard domain problems, there’s also different tradeoffs in solutions. Do we want compile-time or runtime correctness? Should units be part of the type system or is there a better representation? How do we handle derived units? How do we handle generics? A good overview of the design challenges is here, which discusses some of the solutions proposed for Ada.
This page covers how various languages approach measurements. For the most part, they punt the question to preprocessors and libraries, limiting how much they can help you. The only mainstream language with built-in support for units of measure is F#. F# focuses on catching all errors at compile time. All conversions between units must be made explicit, so you can’t accidentally add feet and meters. The price for this is flexibility: you can’t add centimeters and meters, either.
// Mass, grams.<br>[Measure>] type g<br>// Mass, kilograms.<br>[Measure>] type kg
// Define conversion constants.<br>let gramsPerKilogram : floatg kg^-1> = 1000.0g/kg>
// Define...