# \[ANN\] UnitTypes v1.0.0

**URL:** https://discourse.julialang.org/t/ann-unittypes-v1-0-0/126351
**Category:** Package Announcements
**Created:** [February 26, 2025, 6:53pm UTC](https://discourse.julialang.org/t/ann-unittypes-v1-0-0/126351 "2025-02-26T18:53:34Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![bcon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bcon/32/23495_2.png) [@bcon](https://discourse.julialang.org/u/bcon)
#### Post date: [February 26, 2025, 6:53pm UTC](https://discourse.julialang.org/t/ann-unittypes-v1-0-0/126351/1 "2025-02-26T18:53:34Z")

</div>

This package provides physical units as Julia types.

```julia
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
julia> function goFaster(a::T) where T<:AbstractAcceleration end

```

This leads to correctness and very clear error messages.

```julia
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:

```julia-auto
AbstractMeasure
├─ AbstractAcceleration
│ └─ MeterPerSecond2
├─ AbstractAngle
│ ├─ Degree
│ └─ Radian
├─ AbstractArea
│ ├─ Acre
│ ├─ Meter2
│ ├─ SquareFoot
│ └─ SquareMile
├...and so on

```

See the [docs](https://mechanomy.github.io/UnitTypes.jl/dev/) 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](https://painterqubits.github.io/Unitful.jl/latest/) 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](https://painterqubits.github.io/Unitful.jl/latest/trouble/#promotion-with-dimensionless-numbers), as they are not first-class entities but rather ratios, and rather lengthy type unions that clutter outputs, especially on error:

```julia
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](https://github.com/mechanomy/ExchangeUnitful.jl) to enable interoperation with Unitful.

## DynamicQuantities

[DynamicQuantities](https://github.com/SymbolicML/DynamicQuantities.jl) 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](https://github.com/mechanomy/UnitTypes.jl/issues/new)!**

---

<div class="post-metadata">

### Author: ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)
#### Post date: [February 26, 2025, 10:29pm UTC](https://discourse.julialang.org/t/ann-unittypes-v1-0-0/126351/2 "2025-02-26T22:29:02Z")

</div>

Trying out alternative unit designs is nice! Unitful.jl is great and covers almost all usecases, but it’s by no means perfect. I definitely have a set of gripes related to it 🙂

Still, I wonder if

> [@bcon](#):
>
> rather lengthy type unions that clutter outputs, especially on error

can potentially be solved with minor modifications to Unitful.jl design? Eg, simplify types or introduce shorter type aliases for common cases.

Regarding

> [@bcon](#):
>
> 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.

Is it potentially possible in the future to support arbitrary units (or SI prefixes) without defining each of them manually? Or this is a fundamental limitation?

Btw,

> [@bcon](#):
>
> [DynamicQuantities](https://github.com/SymbolicML/DynamicQuantities.jl) is newer and faster than Unitful

is not universally the case: Unitful.jl is generally faster when the code is type-stable – that is, unless one has runtime-dynamic units.

---

<div class="post-metadata">

### Author: ![mcabbott](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mcabbott/32/6603_2.png) [@mcabbott](https://discourse.julialang.org/u/mcabbott)
#### Post date: [February 26, 2025, 11:18pm UTC](https://discourse.julialang.org/t/ann-unittypes-v1-0-0/126351/3 "2025-02-26T23:18:51Z")

</div>

Nice to see more exploration. I too wonder how much of the benefit of nicely named abstract types could be had by defining nicely named aliases – leaving more complex units (like x^4) ugly but working:

```julia
julia> abstract type MyAbstractMeasure{L,T,M,Etc} end

julia> const MyAbstractVolme = MyAbstractMeasure{3,0,0,0};

julia> struct MyMeter3 <: MyAbstractVolme; x::Float64; end

julia> ff(x::MyAbstractVolme) = "yes, it's a volume";

julia> ff(x)
ERROR: MethodError: no method matching ff(::Meter)

Closest candidates are:
  ff(::MyAbstractVolme)
   @ Main REPL[50]:1

```

Trying to define such aliases for Unitful doesn’t affect method errors, I’m not really sure why. (Edit, from your `Unitful.Acceleration` I learn that Unitful already has such aliases, but they also don’t show up.)

```julia
julia> using Unitful: Quantity, m, ft, 𝐋

julia> const UAbsVol{T} = Quantity{T, 𝐋^3}
Quantity{T, 𝐋³} where T

julia> gg(x::UAbsVol) = "yes it's a volume"

julia> gg(1ft^3)
"yes it's a volume"

julia> gg(1m)
ERROR: MethodError: no method matching gg(::Quantity{Int64, 𝐋, Unitful.FreeUnits{(m,), 𝐋, nothing}})

Closest candidates are:
  gg(::Quantity{T, 𝐋³} where T)
   @ Main REPL[21]:1

julia> hh(::Unitful.Volume) = "yes it's a volume";

julia> hh(1m)
ERROR: MethodError: no method matching hh(::Quantity{Int64, 𝐋, Unitful.FreeUnits{(m,), 𝐋, nothing}})

Closest candidates are:
  hh(::Union{Quantity{T, 𝐋³, U}, Level{L, S, Quantity{T, 𝐋³, U}} where {L, S}} where {T, U})
   @ Main REPL[31]:1

```

---

<div class="post-metadata">

### Author: ![Jake](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jake/32/46007_2.png) [@Jake](https://discourse.julialang.org/u/Jake)
#### Post date: [February 27, 2025, 12:33am UTC](https://discourse.julialang.org/t/ann-unittypes-v1-0-0/126351/4 "2025-02-27T00:33:31Z")

</div>

I have not experimented too much with units in Julia, but like the concept and like the concept of SI prefixes. InspectDR.jl has dependency, NumericIO.jl, which outputs SI prefixes (among other things). It might be interesting to see if this would be useful in these applications.

---

<div class="post-metadata">

### Author: ![bcon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bcon/32/23495_2.png) [@bcon](https://discourse.julialang.org/u/bcon)
#### Post date: [February 27, 2025, 2:37am UTC](https://discourse.julialang.org/t/ann-unittypes-v1-0-0/126351/5 "2025-02-27T02:37:56Z")

</div>

> [@aplavin](#):
>
> Is it potentially possible in the future to support arbitrary units (or SI prefixes) without defining each of them manually?

The inverse mapping of a unit label into a fuller type is the issue; “m” → Meter is hard when the package doesn’t know that “m” stands for Meter. But I’m certainly open to having a large library of predefined, common symbols.

SI prefixes could be expanded in u\_str ( 1.2u"GB" =\> 1.2e9Bytes ) and assumed in measure2String() (1.2e9 Bytes =\> 1.2GB) but I feel this would quickly devolve into printf-like format strings.

---

<div class="post-metadata">

### Author: ![bcon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bcon/32/23495_2.png) [@bcon](https://discourse.julialang.org/u/bcon)
#### Post date: [February 27, 2025, 2:48am UTC](https://discourse.julialang.org/t/ann-unittypes-v1-0-0/126351/6 "2025-02-27T02:48:32Z")

</div>

> [@aplavin](#):
>
> …can potentially be solved with minor modifications to [Unitful.jl](https://juliaregistries.github.io/General/packages/redirect_to_repo/Unitful) design? Eg, simplify types or introduce shorter type aliases for common cases.

There’s been plenty of discussion [here](https://discourse.julialang.org/t/physical-units-in-julia/76825/19) and [here](https://discourse.julialang.org/t/typed-units-instead-of-parameterized-unitful-or-exponent-tracking-dynamicquantities/105735/4) but much of my point is that Unitful’s design is overly complicated for many use cases, compared to the straightforward approach here. That first thread mentions a few packages that tackle Unitful’s verbosity, but it’s still there underneath. And fixing the display verbosity doesn’t give you clear, simple types.

---

<div class="post-metadata">

### Author: ![jkroso](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jkroso/32/220909_2.png) [@jkroso](https://discourse.julialang.org/u/jkroso)
#### Post date: [February 27, 2025, 7:20am UTC](https://discourse.julialang.org/t/ann-unittypes-v1-0-0/126351/7 "2025-02-27T07:20:39Z")

</div>

I’ve written a Unit library with a similar type hierachy to this. It just makes a lot of sense conceptually so I keep using it over other libraries.

You might want to [check out](https://github.com/jkroso/Units.jl) how I defined composite units because it enables you to mix your approach and the Unitfull approach without any correctness issues. It’s just a question of picking which composite types you want to define a special type for. In my library Volt and Angle have special types defined while Speed doesn’t so Speed shows up in stack traces as `Length/Time` while Volt just shows up as `V`
