This package provides physical units as Julia types.
julia> using UnitTypes
julia> x = Meter(3)
3m
julia> typeof(x)
Meter
julia> typeof(x) <: AbstractLength
true
julia> typeof(x) <: AbstractCapacitance
false
This allows you to easily write functions with arguments restricted to variables having certain types.
julia> function goFaster(a::T) where T<:AbstractAcceleration end
This leads to correctness and very clear error messages.
julia> goFaster(3u"m/s")
ERROR: MethodError: no method matching goFaster(::MeterPerSecond)
Closest candidates are:
goFaster(::AbstractAcceleration)
Type hierarchy
UnitTypes introduces an abstract type hierarchy of:
AbstractMeasure
├─ AbstractAcceleration
│ └─ MeterPerSecond2
├─ AbstractAngle
│ ├─ Degree
│ └─ Radian
├─ AbstractArea
│ ├─ Acre
│ ├─ Meter2
│ ├─ SquareFoot
│ └─ SquareMile
├...and so on
See the docs for more.
Macros are used to introduce and create relationships around new types:
@makeBaseMeasure Length Meter "m"
- introduces a new basic Measure like Meter for Length or Meter3 Volume, this should be rarely used!@makeMeasure Meter(1000) = KiloMeter(1) "km"
- derives a new measure (KiloMeter) from some an existing measure (Meter) with a conversion ratio (1000m = 1km)@relateMeasures KiloGram*MeterPerSecond2=Newton
- relates the product of types to another type, all types preexisting.
Comparison with other packages
Unitful
Unitful leverages parametric types to store units, giving flexibility at the cost of compile-time type uncertainty. It’s two major limitations are the avoidance of angular measures, as they are not first-class entities but rather ratios, and rather lengthy type unions that clutter outputs, especially on error:
julia> function goSlower(x<:Unitful.Acceleration) end
goSlower (generic function with 1 method)
julia> goSlower(1u"mm")
ERROR: MethodError: no method matching goSlower(::Quantity{Int64, 𝐋 , Unitful.FreeUnits{(mm,), 𝐋 , nothing}})
Closest candidates are:
goSlower(::T) where T<:(Union{Quantity{T, 𝐋 𝐓^-2, U}, Level{L, S, Quantity{T, 𝐋 𝐓^-2, U}} where {L, S}} where {T, U})
As Unitful is the dominant unit package and has wide use and support, we provide a separate package ExchangeUnitful to enable interoperation with Unitful.
DynamicQuantities
DynamicQuantities is newer and faster than Unitful because it “defines a simple statically-typed Quantity type for storing physical units.” It does this by storing the exponents on the basic units, allowing any unit traceable to SI to be used. But this performant representation hurts readability, and while the unit representation may be able to be hidden behind overrides of show(), Julia is designed for types to be read and manipulated directly by users.
Enter UnitTypes
In the presence of Julia’s type-first UI, these two, good attempts feel misdirected and motivate this package’s literal typing of units. The limitation is that UnitTypes does not have a catch-all unit representation. Only units that have been defined by one of the macros may be represented, and complex units may need to have additional methods written to correctly convert between units.
Corrections, suggestions, contributions, and questions are welcome here or in an issue!