Units and Measurements
Units and measurements in Racket, with conversion facilities between units.
This collection has not been extensively tested. Use with caution and please report any error that you find.
Be cautious with non-linear converters (e.g., °F to K), as converting a temperature difference is not the same as converting a temperature.
Some bindings from racket may be redefined, like second, min and drop. You can use rename-in to change these name on require.
1 Quick example
> (define my-speed (m* 50.0 mile (m/ hour))) > (measure->value my-speed) '(22.352 m (s -1))
> (measure->value (convert my-speed '(km (h -1)))) '(80.46719999999999 km (h -1))
> (measure->value (convert (m* my-speed 5 min) 'km)) '(6.7056000000000004 km)
> (measure->value (convert (m/ (m* 21.0 mi) (m* 13 min)) '(mi (h -1)))) '(96.9230769230769 mi (h -1))
2 Basic definitions
A unit is a symbol and an exponent. A measure is a number and a set of units.
Basic arithmetic operations (m+ m- m* m/ m^) are defined to work with measures.
a (struct) measure,
a number,
a DSL unit,
a list with a number followed by one or more DSL units.
a (struct) unit,
a symbol alone (taking the exponent 1 by default),
a list with a symbol and an exponent.
You can use the multiplication operator m* to easily build measures.
> (m* 3) (measure 3 (set))
> (m* 3 's) (measure 3 (set (unit 's 1)))
> (m* 3 's '(m -1)) (measure 3 (set (unit 'm -1) (unit 's 1)))
> (m+ 2 3) (measure 5 (set))
> (m/ 3 '(2 s)) (measure 3/2 (set (unit 's -1)))
> (measure->value (m* '(3 s) 5 '(10 m))) '(150 m s)
> (measure->value (m* '(3 s) '(5 (s -1)))) 15
> (measure->value (m+ '(3 m (h -1)) '(2 m h))) Error: Measures must have the same units.
Got: #<set: #(struct:unit h 1) #(struct:unit m 1)> and
#<set: #(struct:unit m 1) #(struct:unit h -1)>
> (measure->value (m+ '(3 m (h -1)) '(2 m (h -1)))) '(5 m (h -1))
3 Units and conversions
> mmHg (measure 166653/1250 (set (unit 'm -1) (unit 's -2) (unit 'kg 1)))
> millimetre-of-mercury (measure 166653/1250 (set (unit 'm -1) (unit 's -2) (unit 'kg 1)))
By default, all units are converted to SI units. This allows to perform dimension reductions when possible.
> N (measure 1 (set (unit 's -2) (unit 'kg 1) (unit 'm 1)))
> Pa (measure 1 (set (unit 'm -1) (unit 's -2) (unit 'kg 1)))
> (m/ (m* 3 N) (m* 2 Pa)) (measure 3/2 (set (unit 'm 2)))
> (m* 3 mi) (measure 603504/125 (set (unit 'm 1)))
> (m+ (m* 3 mi) (m* 2 m)) (measure 603754/125 (set (unit 'm 1)))
> (m* 3 'mi) (measure 3 (set (unit 'mi 1)))
> (m+ '(5 mi) (m* 2 '(3 mi))) (measure 11 (set (unit 'mi 1)))
> (equal? (m* 3 m (m/ 1 s s)) (m* '(3 m (s -2)))) #t
> (m+ (m* 3 'mi) (m* 2 'm)) Error: Measures must have the same units.
Got: #<set: #(struct:unit m 1)> and #<set: #(struct:unit mi
1)>
> (convert (m* 3 'mi)) (measure 603504/125 (set (unit 'm 1)))
> (convert (m* 3 m) 'mile) (measure 125/67056 (set (unit 'mi 1)))
> (convert (m* 3 ft (m/ s)) '(mi (h -1))) (measure 45/22 (set (unit 'mi 1) (unit 'h -1)))
> (convert (m* 10 hecto Pa) 'mmHg) (measure 1250000/166653 (set (unit 'mmHg 1)))
> (m* 2 Pa 3 m m) (measure 6 (set (unit 's -2) (unit 'kg 1) (unit 'm 1)))
> (convert (m* 2 Pa 3 m m) 'N) (measure 6 (set (unit 'N 1)))
> (measure->value (convert (m* 3 kilo Pa) '(hecto Pa))) '(30 Pa h.)
Prefixes are followed by a dot to avoid name collision with units.
The order of "units" is first by exponent then alphabetical (ASCII), this is why the h. is after Pa.
the 'base symbol (default), to convert to base (SI by default) units,
a DSL unit,
a list of symbols and DSL units.
> (convert (m* 3 'mi) 'yd) (measure 1250/381 (set (unit 'm -1) (unit 'mi 1) (unit 'yd 1)))
> (convert (m* 3 'mi) '(base yd)) (measure 5280 (set (unit 'yd 1)))
> (convert (m* 3 mi) 'yd) (measure 5280 (set (unit 'yd 1)))
4 Dimensions and contracts
Units and measures are organized in dimensions.
> (define/contract (speed a-distance a-time) (length/c time/c . -> . velocity/c) (m/ a-distance a-time)) > (speed (m* 5 mile) (m* 2 hour)) (measure 1397/1250 (set (unit 's -1) (unit 'm 1)))
> (speed (m* 5 mile) (m* 2 metre)) speed: contract violation
expected: time/c
given: (measure 2 (set (unit 'm 1)))
in: the 2nd argument of
(-> length/c time/c velocity/c)
contract from: (function speed)
blaming: top-level
(assuming the contract is correct)
at: eval:37.0
5 A ’measures’ language
The measures/lang language can be used as a short-hand to have all of racket plus all of of measures except that the measures arithmetic operators (m+, etc.) replace the normal ones (+, etc.).
racket -li measures/lang
6 Chemical elements
The measures/chemical-elements provides the vector elements of the 118 elements with a number of procedures to extract their information: atomic-number atomic-symbol chemical-element group period atomic-weight density melting-point boiling-point heat-capacity electronegativity abundance.
Each procedure accepts either a number (the atomic number) or a symbol (either the atomic symbol or the name of the chemical element).
> (require measures/chemical-elements) > (atomic-number 'Oxygen) 8
> (atomic-symbol 'Iron) 'Fe
> (atomic-symbol 2) 'He
> (chemical-element 'Na) 'Sodium
> (atomic-weight 'Carbon) (measure 1.99447483422e-26 (set (unit 'kg 1)))
> (m* 3 cl (density 'Mercury)) (measure 0.40600800000000004 (set (unit 'kg 1)))
7 Related resources
Some useful conversions can be found on Wikipedia (to be trusted with caution of course).
This collection was partly inspired by the Frink programming language and Konrad Hinsen’s Clojure units library.
See also AlexKnauth’s measures-with-dimensions.
You may also be interested in Doug Williams scientific collection.
8 License and Disclaimer
Copyright (c) 2013 Laurent Orseau
Licensed under the GNU LGPL. See LICENSE.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |