Module SIunits

module SIunits: sig .. end
Numerical values with units enforced at run-time.

SIunits provides run-time unit checking and automatic unit conversion.

Read the overview for an introduction to the features of this library, and consult the descriptions of the operators for further details.

Unless you require a large number of custom units, you should only need to open SIunits.Ops and SIunits.Common in order to use this library.


module Unit: sig .. end
Definition of units.
module Value: sig .. end
Creation and conversion of numerical quantities.
module Ops: sig .. end
Grouped infix operators.
module Common: sig .. end
A collection of commonly-used units.


The two important types provided by SIunits are the unit definitions (SIunits.Unit.t) and values (SIunits.Value.t) that associate a numerical quantity with a particular set of units.

Defining units

Units can be defined as combinations (SIunits.Unit.d) of the basic SI units (SIunits.Unit.u), or by taking the product (SIunits.Unit.mult) or ratio (SIunits.Unit.div) of existing units:

let kph = %/ Common.hour in ...

Non-SI units can also be defined as scalar multiples of existing units. For example, millimetres of mercury (mmHg) can be defined in terms of pascals, which are defined in SIunits.Common:

let mmHg = Unit.scale 133.322387415 Common.pascal in ...

Units such as (hour)-1 can be defined using (min)-1:

let per_min = Unit.(per [Minute]) in
(* 1 per hour = 1/60 per minute *)
let per_hour = Unit.scale (1.0 /. 60.0) per_min in ...

Manipulating numerical quantities

Numerical quantities can then be associated with units and scaled to other units (which must have compatible dimensions):

let speed1 = 60.0 %< /: (2.0 %< Common.hour)
and speed2 = 30.0 %< kph (* equivalent to the line above *) in
let m_per_s = speed1 %> Unit.(ratio [Metre] [Second]) in ...

let pressure = 110.0 %< Common.mmHg %> Common.kilo_pascal in ...

Numerical quantities can be added and subtracted if their units are compatible and can also be multiplied or divided:

let dist1 = 5.0 %<
and dist2 = 212.0 %< Common.metre in
let sum = dist1 +: dist2
and area = dist1 *: dist2 in
let sum_cm = sum %> (* 521200.0 cm *)
and area_km2 = area %> ( %* (* 1.06 km^2 *)
in ... 

Mutable quantities

Mutable values (of type Value.t ref) can be updated and referenced with the <-: and !: operators, respectively. The update operator will retain the units of the reference's original value.

let x_in_m = ref (2.0 %< Common.metre) in
x_in_m <-: (1.9 %<;
!: x_in_m = 1900.0 (* evaluates to true *)

Invalid units

An exception is thrown when two values with incompatible units are combined, and when a value is converted into a set of incompatible units:

let a = 5.0 %< Common.metre
and b = 1.0 %< Common.min in
a +: b (* raises Value.Invalid_units (a, b, "+:") *)

let a = 5.0 %< Common.metre in
a %> Common.min (* raises Value.Invalid_conversion (a, units) *)


Common defines several temperature conversion functions. Note that arithmetic operations are performed with respect to degrees Kelvin and will not produce values consistent with arithmetic in degrees Celsius or degrees Fahrenheit:

let t1 = from_celsius 30.0
and t2 = from_celsius 10.0 in
to_celsius (t1 +: t2) (* 313 degrees Celsius *)

This is because they do not have the same zero-point as degrees Kelvin. Arithmetic with non-SI units that have the same zero-point as the SI units (e.g., inches, mmHg) will work as expected.


Operators are defined in SIunits.Unit and SIunits.Value, and are collected in SIunits.Ops for convenience.

The convention is that operators beginning with % are concerned with units (specifically unit creation, unit equivalence and unit conversion); operators ending with : are concerned with values (specifically arithmetic operations and unit/numerical equality); operators ending with :. perform scalar operations on values.

Mutable values (Value.t ref) are manipulated with the !: and <-: operators, which respectively return and update the value. Both operators retain the original units associated with the mutable value. The <-: operator raises Invalid_units (v1, v2, "<-:") if the units of the original and new values are incompatible.

The precedence and associativity of the operators are defined in the OCaml manual. All binary operators are left-associative and the order of precedence is as per the standard arithmetic operators: !: has the highest precedence, followed by the unit operators and value multiplication and division, then the value addition and subtraction operators, and finally <-: and the equality operators.

This is why unit conversion can be written as:

let pressure = 110.0 %< Common.mmHg %> Common.kilo_pascal in ...

But the product or ratio of two values must be written as:

let speed1 = 60.0 %< /: (2.0 %< Common.hour) in ...