Okay this is probably my worst instance of Yak shaving yet, but I have now turned this into a new package:

Rather than using a `Dict{Symbol,Bool}`

as above, it uses a fixed struct to store dimensions. There are 7 fields in this struct, corresponding to the 7 SI units.

Here’s the README:

This defines a simple statically-typed `Quantity`

type for Julia.

Physical dimensions are stored as a *value*, as opposed to a parametric type, as in Unitful.jl.

This is done to allow for calculations where physical dimensions are not known at compile time.

## Performance

DynamicUnits can greatly outperform Unitful

when the compiler cannot infer dimensions in a function:

```
julia> using BenchmarkTools, DynamicUnits; import Unitful
julia> dyn_uni = Quantity(0.2, mass=1, length=0.5, amount=3)
0.2 𝐋 ¹ᐟ² 𝐌 ¹ 𝐍 ³
julia> unitful = convert(Unitful.Quantity, dyn_uni)
0.2 kg m¹ᐟ² mol³
julia> f(x) = x ^ rand(1:10) * 0.3;
julia> @btime f($dyn_uni);
80.449 ns (0 allocations: 0 bytes)
julia> @btime f($unitful);
29.666 μs (42 allocations: 1.91 KiB)
```

(Note the μ and n.)

Here, the DynamicUnits quantity object allows the compiler to build a function that is type stable,

while the Unitful quantity object, which stores its dimensions in the type, requires type inference at runtime.

However, if the dimensions in your function *can* be inferred by the compiler,

then you are better off using Unitful:

```
julia> g(x) = x ^ 2 * 0.3;
julia> @btime g($dyn_uni);
56.317 ns (0 allocations: 0 bytes)
julia> @btime g($unitful);
1.958 ns (0 allocations: 0 bytes)
```

While both of these are type stable,

because Unitful parametrizes the type on the dimensions, functions can specialize

to units and the compiler can optimize away units from the code.

## Usage

You can create a `Quantity`

object with a value and keyword arguments for the powers of the physical dimensions

(`mass`

, `length`

, `time`

, `current`

, `temperature`

, `luminosity`

, `amount`

):

```
julia> x = Quantity(0.3, mass=1, length=0.5)
0.3 𝐋 ¹ᐟ² 𝐌 ¹
julia> y = Quantity(10.2, mass=2, time=-2)
10.2 𝐌 ² 𝐓 ⁻²
```

Elementary calculations with `+, -, *, /, ^, sqrt, cbrt`

are supported:

```
julia> x * y
3.0599999999999996 𝐋 ¹ᐟ² 𝐌 ³ 𝐓 ⁻²
julia> x / y
0.029411764705882353 𝐋 ¹ᐟ² 𝐌 ⁻¹ 𝐓 ²
julia> x ^ 3
0.027 𝐋 ³ᐟ² 𝐌 ³
julia> x ^ -1
3.3333333333333335 𝐋 ⁻¹ᐟ² 𝐌 ⁻¹
julia> sqrt(x)
0.5477225575051661 𝐋 ¹ᐟ⁴ 𝐌 ¹ᐟ²
julia> x ^ 1.5
0.1643167672515498 𝐋 ³ᐟ⁴ 𝐌 ³ᐟ²
```

Each of these values has the same type, thus obviating the need for type inference at runtime.

Furthermore, we can do dimensional analysis automatically:

```
julia> x + 3 * x
1.2 𝐋 ¹ᐟ² 𝐌 ¹
julia> x + y
INVALID
```

We can see the second one has `valid(quantity) == false`

. This doesn’t throw an error by default, as it allows for stable return values.

The dimensions of a `Quantity`

can be accessed either with `dimension(quantity)`

for the entire `Dimensions`

object:

```
julia> dimension(x)
𝐋 ¹ᐟ² 𝐌 ¹
```

or with `umass`

, `ulength`

, etc., for the various dimensions:

```
julia> umass(x)
1//1
julia> ulength(x)
1//2
```

Finally, you can strip units with `ustrip`

:

```
julia> ustrip(x)
0.2
```

## Units

Despite the name, DynamicUnits does not actually work with units. Instead, it works with *dimensions*.

You can use Unitful to parse units, and use the DynamicUnits->Unitful extension for conversion:

```
julia> using Unitful: Unitful, @u_str
julia> x = 0.5u"km/s"
0.5 km s⁻¹
julia> y = convert(DynamicUnits.Quantity, x)
500.0 𝐋 ¹ 𝐓 ⁻¹
julia> y2 = y^2 * 0.3
75000.0 𝐋 ² 𝐓 ⁻²
julia> x2 = convert(Unitful.Quantity, y2)
75000.0 m² s⁻²
julia> x^2*0.3 == x2
true
```

## Vectors

There is not a separate class for vectors, but you can create units

like so:

```
julia> randn(5) .* Dimensions(mass=2/5, length=2)
5-element Vector{Quantity{Float64}}:
-0.6450221578668845 𝐋 ² 𝐌 ²ᐟ⁵
0.4024829670050946 𝐋 ² 𝐌 ²ᐟ⁵
0.21478863605789672 𝐋 ² 𝐌 ²ᐟ⁵
0.0719774550969669 𝐋 ² 𝐌 ²ᐟ⁵
-1.4231241943420674 𝐋 ² 𝐌 ²ᐟ⁵
```

Because it is type stable, you can have mixed units in a vector too:

```
julia> v = [Quantity(randn(), mass=rand(0:5), length=rand(0:5)) for _=1:5]
5-element Vector{Quantity{Float64}}:
2.2054411324716865 𝐌 ³
-0.01603602425887379 𝐋 ⁴ 𝐌 ³
1.4388184352393647
2.382303019892503 𝐋 ² 𝐌 ¹
0.6071392594021706 𝐋 ⁴ 𝐌 ⁴
```