This SRFI describes numeric procedures and constructors for quantities, which are numbers that have an associated unit. The specified procedures are similar to Scheme’s numerical procedures, but allow for dimensional analysis during type-checking. The ability to create new quantities, procedures to convert quantities to different types, and libraries for standardized or common quantities are also provided.
- Currently conversions are 1-directional (towards base unit), need a way to make them bidirectional
- The included imperial/US units were selected based on present-day usage. Historical units or units specific to a commodity (alcohol, oil, soybean etc.) are excluded. Have I forgotten any? Are there other special unit systems that would be useful to include?
- Should shannon and bit be compatible?
Numeric quantities are ubiquitous in scientific software, and in any software that interacts with the outside world. However, the numerical towers of most programming languages (including Scheme) have no concept of units, and programmers instead must use simple numbers to represent quantities, sometimes with code comments to indicate the intended unit. This can lead to a class of errors where procedures expect numeric arguments that represent a quantitity with a certain unit, but instead receive numbers that represent a different unit.
With the introducton of SRFI 253: Data (Type-)Checking, Scheme can now check the validity of procedure arguments, ensuring that they match a given predicate. This is one of the prerequisites towards eliminating unit conversion errors. The other prerequisite is the existence of numeric types for quantities: that is the goal of this SRFI.
This SRFI provides an extensive set of commonly-used units, encompassing both standard SI units and specialized systems of measurement. While it is possible to define custom units, it is expected that the provided units will meet the needs of the vast majority of programs. This SRFI can only guarantee dimensional correctness for operations that use the provided units exclusively.
Implementation of this SRFI does not require modification of the existing Scheme numeric tower or type system. Quantity arithmetic is performed using special procedures that are similar to the arithmetic procedures provided by R7RS-small. wHaving separate arithmetic procedures for quantities provides a greater degree of safety.
Some programming languages provide quantity types in the base language, or more commonly, as an external library. The following implementations were referenced during the creation of this SRFI:
- Kawa Scheme quantities
- The proposed C++ quantities and units library
- The uom Rust library
- The pint Python library
- The measures Racket library
Quantity checking should be implemented using SRFI 145 assumptions or SRFI 253 type checking forms, so that debug mode behavior remains consistent.
(make-quantity num unit)
Return a quantity object for a given number and unit. An error is signalled if the number argument is infinite or NaN.
(quantity? object)
Return #t
if object
is a quantity, #f
otherwise.
(quantity-number quantity)
Return the number component of a quantity, a Scheme number.
(quantity-unit quantity)
Return the unit component of a quantity, a unit record.
(quantity=? quantity quantity2 . rest)
Return #t
if all the supplied quantities are equal, when the quantities are
converted to a common unit. Otherwise, returns #f
. An error shall be signalled
if any of the supplied arguments is not a quantity.
(strict-quantity=? quantity quantity2 . rest)
Return #t
if all the supplied quantities are equal, and the units of the
quantities are all the same. Otherwise, returns #f
. An error shall be
signalled if any of the supplied arguments is not a quantity.
Strict equality does not consider the name, abbreviation, or other auxillary properties of the unit. For example, kilometer and an alias for kilometer would be strictly equal, but kilometer and meter would not.
(quantity-predicate unit)
Returns a predicate procedure that accepts one argument of any type, and returns
#t
if the received argument is a quantity with unit compatible with that of
the supplied unit
argument. Otherwise, the predicate procedure must return
#f
.
(strict-quantity-predicate unit)
Returns a predicate procedure that accepts one argument of any type, and returns
#t
if the received argument is a quantity with a unit that is the same as that
of the supplied unit
argument. Otherwise, the predicate procedure must return
#f
.
(convert-quantity quantity new-unit)
Returns a quantity with unit new-unit
, that is equivalent to the supplied
quantity
argument. If the unit of the quantity
argument and new-unit
are
the same, the quantity
argument is returned. If the quantity
argument cannot
be converted to new-unit
, an error shall be signalled.
(quantity->string quantity)
Converts any quantity value to a formatted string. This procedure must use the unabbreviated form of unit names. This procedure should represent the number using a unit prefix, but the unit prefix must also be unabbreviated.
(quantity->string (qn* (make-quantity 1000 meter)
(qn* (make-quantity 1 second)
(make-quantity 1 second))))
;; => "1000 kilometers / second^2"
(quantity->short-string quantity)
Converts any quantity value to a formatted string. This procedure should use the abbreviated form of unit names. This procedure may represent the quantity using an abbreviated unit prefix.
(quantity->short-string (qn* (make-quantity 1000 meter)
(qn* (make-quantity 1 second)
(make-quantity 1 second))))
;; => "1 km / s^2"
(make-unit name abbreviation dimension base-unit converter)
Return an object representing a unit. The name
argument is a string containing
the uncapitalized full name of the unit. The abbreviation
argument is a string
containing the abbreviation of the unit, without punctuation. The base unit
argument is the unit from which the specified unit is derived.
The dimension
argument is a list of dimension objects that represent the base
quantities of what the unit measures. See the Dimensions
section for details.
The converter
argument is a procedure that accepts a single argument, a Scheme
number. It must convert its numeric argument into the equivalent number of the
base unit. If a number argument is invalid or out of range for the specified
unit, the procedure is permitted to signal an error, in which case quantity
conversions and arithmetic operations will also signal an error.
The accessor functions unit-name
, unit-abbreviation
, unit-dimension
,
unit-base-unit
, unit-converter
shall be made available by the SRFI library.
;; 1 foo = 500 meters
(define foo (make-unit "foo" "f" (unit-dimension meter) meter
(lambda (x) (* x 500))))
(quantity->full-string (quantity-convert (make-quantity 3 foo) kilometer))
;; => "1.5 kilometer"
(unit? object)
Return #t
if object
is a unit, #f
otherwise.
(unit=? unit1 unit2 . rest)
Return #t
if all the supplied unit arguments are the same, #f
otherwise. If
any of the supplied arguments is not a unit, an error shall be signalled.
(units-compatible? unit1 unit2 . rest)
Return #t
if all the supplied unit arguments can be converted to a common
unit. Otherwise, return #f
. If any of the supplied arguments is not a unit, an
error shall be signalled.
Units that can be converted to one another using dimensional analysis are not
necessarily compatible. For example, both hertz and becquerel are both derived
from seconds, but since hertz has the dimension frequency
and becquerel has
the dimension activity
they are not compatible. Units are considered
compatible if they are mathematically convertible, and their list of dimensions
are equal, order notwithstanding.
(unit* unit1 unit2 . rest)
Create a derived unit by multiplying the provided unit arguments. The name, abbreviation, dimension, and other unit fields are automatically generated.
If a change to the derived unit’s auxillary information (name, abbreviation,
etc.) is required, use the derived unit as the base-unit
argument of the
make-unit
procedure.
(define newton-meter (unit* newton meter))
(define foobar (make-unit "foobar" "fb" (unit-dimension newton-meter)
newton-meter
(lambda (x) x)))
(unit/ unit1 unit2 . rest)
Create a derived unit by dividing the provided unit arguments. The name, abbreviation, dimension, and other unit fields are automatically populated.
(define meters-per-second-squared (unit/ meter (unit* second second)))
unitless
A predefined unit that represents unitless constants. Can be used as a base unit.
ratio
A predefined unit that represents ratios between two quantities. Can be used as a base unit.
angle
A predefined unit that represents angles. Can be used as a base unit.
(make-dimension symbol inverse?)
Returns a new dimension record. The symbol
argument is the symbol that
represents the dimension, such as 'length
. The inverse?
argument is a
boolean that indicates whether or not the dimension is a reciprocal. The
accessor procedures dimension-symbol
and dimension-inverse?
shall be made
available by the SRFI library.
(dimension=? dim1 dim2 . rest)
Returns #t
if the provided dimension arguments are equal, #f
otherwise.
Dimensions are equal if their symbol
and inverse?
fields are equal by the
eq?
comparator. An error is signalled if any of the supplied arguments are not
dimensions.
dimensionless
A dimension to represent dimensionless quantities.
count
A dimension to represent a count of something.
(qn+ quantity quantity2 . rest)
Add two or more quantities with compatible units, and return a new quantity. The unit of the returned quantity must be compatible with those of all of the provided arguments, but no further requirements regarding the unit conversion are specified. An error is signalled if the units of any argument are incompatible.
Quantities shall be converted to the common unit before addition occurs. For example, adding 10 degrees Celsius to 10 Kelvin does not result in 20 Kelvin. Instead, the 10 degrees Celsius is converted to 283.15 Kelvin, then that quantity is added to the 10 Kelvin quantity.
(qn- quantity quantity2 . rest)
Subtract two or more quantities with compatible units, and return a new quantity. The unit of the returned quantity must be compatible with those of all of the provided arguments, but no further requirements regarding the unit conversion are specified. An error is signalled if the units of any argument are incompatible.
(qn* quantity quantity2 . rest)
Multiply two or more quantities, creating a quantity with a derived unit, that is based on the units of its arguments.
(qn/ quantity quantity2 . rest)
Divide two or more quantities, creating a quantity with a derived unit that is based on the units of its arguments.
The following procedures shall be made available, and provide quantity-specific equivalents to the corresponding R7RS-small procedures. The corresponding R7RS-small procedure can be determined by removing the qn- prefix from the identifier.
qn< qn<= qn=> qn> qn>= qn-abs qn-ceiling qn-even? qn-exact qn-exact-integer-sqrt qn-expt qn-floor qn-floor-quotient qn-floor-remainder qn-floor/ qn-inexact qn-max qn-min qn-modulo qn-negative? qn-numerator qn-odd? qn-positive? qn-quotient qn-rationalize qn-remainder qn-round qn-square qn-truncate qn-truncate-quotient qn-truncate-remainder qn-truncate/ qn-zero?
The following dimensions shall be provided in the (srfi 2xx isq)
library:
length mass time current temperature amount-of-substance luminous-intensity
The following dimensions shall be provided in the (srfi 2xx radiation)
library:
activity absorbed-dose equivalent-dose
The following units shall be provided in the (srfi 2xx si)
library:
ampere becquerel candela celsius coulomb farad gram gray henry hertz joule katal kelvin lumen lux meter mole newton ohm pascal radian second siemens sievert steradian tesla volt watt weber
Each of the following prefixes shall be applied to each of the above units to create prefixed units:
quetta ronna yotta zetta exa peta tera giga mega kilo hecto deca deci centi milli micro nano pico femto atto zepto yocto ronto quecto
These units should be implemented as defined by the latest BIPM SI brochure.
The following units shall be provided in the (srfi 2xx metric)
library:
minute hour day arcminute arcsecond astronomical-unit atmosphere bar bel decibel carat dalton degree electronvolt fermi hectare jansky liter millimeter-of-mercury neper parsec tonne torr
These units should be implemented as defined by the latest BIPM SI brochure and/or the latest revisions of NIST Special Publication 881 and 330.
The following units shall be provided in the (srfi 2xx information)
library:
bit trit nibble byte dit
Each of the SI unit prefixes greater than or equal to kilo- shall be applied to each of the above units.
Each of the following prefixes shall also be applied to each of the (unprefixed) units above:
yobi zebi exbi pebi tebi gibi mebi kibi
The following units shall also be provided:
ban deciban shannon nat hartley
The following dimensions shall be provided:
data entropy
These units and dimensions should be implemented as defined by the latest revision of IEC 80000-13.
The following units shall be provided in the (srfi 2xx imperial)
library:
acre bushel cable chain cup dram fathom fluid-ounce foot furlong gallon grain hand hundredweight inch knot link long-ton mile nautical-mile paris-point pennyweight pint quart rod slug stone tablespoon teaspoon thou troy-ounce troy-pound yard
These units should be implemented as defined by the latest version of the British Weights and Measures Act.
The following units shall be provided in the (srfi 2xx us-customary)
library:
acre bushel cable chain cup dram farenheit fathom fluid-ounce foot furlong gallon grain hand hundredweight inch inch-of-mercury knot link long-ton mile nautical-mile pennyweight pint quart rankine rod short-ton slug tablespoon teaspoon thou troy-ounce troy-pound yard
These units should be implemented as defined by the latest revision of NIST Handbook 44.
The following units shall be provided in the (srfi 2xx cgs)
library:
barye biot calorie darcy debye dyne emu erg franklin gal gauss gilbert kayser kilocalorie lambert langley line maxwell oersted phot poise rad rem roentgen stilb stokes
These units should be implemented as defined by the latest revisions of NIST Special Publication 881 and 330.
The following units shall be provided in the (srfi 2xx typographic)
library:
big-point cicero didot-point pica point scaled-point twip
These units should be implemented as defined by the latest stable release of the TeX Live typesetting system.
A compliant sample implementation written in R7RS-small is provided. It depends on SRFI 253.