Unchained
                                
                                 Unchained copied to clipboard
                                
                                    Unchained copied to clipboard
                            
                            
                            
                        A fully type safe, compile time only units library.
- Unchained - Compile time only units checking [[https://github.com/SciNim/unchained/workflows/unchained%20CI/badge.svg]]
=Unchained= is a fully type safe, compile time only units library. There is absolutely no performance loss over pure =float= based code (aside from insertion of possible conversion factors, but those would have to be written by hand otherwise of course).
It supports:
- all base SI units
- some imperial units
- all SI prefixes
- arbitrary math with units composing to new units, e.g. (which do not have to be defined previously!), e.g. =10.m * 10.m * 10.m * 10.m * 10.m= without having to predefine a =Meter⁵= type
- automatic conversion between SI prefixes if a mix is used
- ...
Aside from bugs and polish the main thing missing is proper handling of natural units (they exist, but are untested and their unit in terms of =eV= is not implemented).
In no particular order things that either are or need to be supported: #+begin_src nim
The following already work unless TODO (not all units implemented)
block:
defining simple units
let mass = 5.kg let a = 9.81.m•s⁻² block:
addition and subtraction of same units
let a = 5.kg let b = 10.kg check typeof(a + b) is KiloGram check a + b == 15.kg check typeof(a - b) is KiloGram check a - b == 15.kg block:
addition and subtraction of units of the same quantity but different scale
let a = 5.kg let b = 500.g check typeof(a + b) is KiloGram check a + b == 5.5.kg
if units do not match, the SI unit is used!
block:
product of prefixed SI unit keeps same prefix unless multiple units of same quantity involved
let a = 1.m•s⁻² let b = 500.g check typeof(a * b) is Gram•Meter•Second⁻² check typeof(a * b) is MilliNewton check a * b == 500.g•m•s⁻² block: let mass = 5.kg let a = 9.81.m•s⁻²
unit multiplication has to be commutative
let F: Newton = mass * a let F2: Newton = a * mass # TODO
unit division works as expected
check typeof(F / mass) is Meter•Second⁻² check F / mass == a block:
automatic deduction of compound units for simple cases
let force = 1.kg * 1.m * 1.s⁻² echo force # 1 Newton doAssert typeof(force) is Newton block:
conversion between units of the same quantity
let f = 10.N check typeof(f.to(kN)) is KiloNewton check f.to(kN) == 0.01.kN block:
pre-defined physical constants
let E_e⁻_rest: Joule = m_e * c*c # math operations *cannot* use superscripts!
m_e = electron mass in kg
c = speed of light in vacuum in m/s
block:
automatic CT error if argument of e.g. sin, ln are not unit less
let x = 5.kg let y = 10.kg discard sin(x / y) ## compiles gives correct result (~0.48) let x2 = 10.m
sin(x2 / y) ## errors at CT due to non unit less argument
block:
imperial units
let mass = 100.lbs let distance = 100.inch block:
mixing of non SI and SI units (via conversion to SI units)
let m1 = 100.lbs let m2 = 10.kg check typeof(m1 + m2) is KiloGram check m1 + m2 == 55.3592.KiloGram block:
natural unit conversions
let speed = (0.1 * c).toNaturalUnit() # fraction of c, defined in constants
let m_e = 9.1093837015e-31.kg.toNaturalUnit()
math between natural units remains natural
let p = speed * m_e # result will be in eV
check p.to(keV) == 51.1.keV
block:
auto conversion of natural units
let a = 10.MeV
let b = 200.eV
check typeof(a / b) is UnitLess # UnitLess is
check a / b == 50_000.Unitless
If there is demand the following kind of syntax may be implemented in the future
when false:
units using english language (using accented quotes)
let a = 10.meter per second squared
let b = 5.kilogram meter per second squared
check typeof(a) is Meter•Second⁻²
check typeof(b) is Newton
check a == 10.m•s⁻²
check b == 5.N
#+end_src
Things to note:
- real units use capital letters and are verbose
- shorthands defined for all typical units
- conversion of numbers to units done using .call and using shorthand names
- •symbol is product of units to allow unambiguous parsing of units
- no division of units, but negative exponents
- exponents are in superscript
- usage of •and superscript is to circumvent Nim's identifier rules!
- SI units are the base. If ambiguous operation that can be solved by unit conversion, SI units are used.
- math operations cannot use superscripts!
- physical units are defined
- conversion from prefixed SI unit to non prefixed SI unit only happens if multiple prefixed units of same quantity involved
- =UnitLess= is a =distinct float= unit that has a converter to =float= (such that =UnitLess= magically works with math functions expecting floats).
- type comparison with =is= is somewhat broken, because it checks for explicit equalness, but not up aliases. Due to that reason we will provide a custom =is= operator that perform type equality checks in the same way as it is done for ~==~.
** Why "Unchained"? Un = Unit Chain = [[https://en.wikipedia.org/wiki/Chain_(unit)][A unit]]
You shall be unchained from the shackles of dealing with painful errors due to unit mismatches by using this lib! Tada!
Hint: The unit =Chain= does not exist in this library...