`AbstractQuantity` subtypes and quantity kinds
The concept
"Defining ‘kind of quantity’" (Flater) and "On (kinds of) quantities" (Mari) discuss a hierarchy of kinds of quantity, in which radius and wavelength are both kinds of quantity that have dimension "length". I think this could be useful for a function that's supposed to take a radius but not a wavelength, or for just keeping track of what's what.
Implementation
Currently, for a type Q <: AbstractQuantity, the *(::Number, ::Q) and / methods return a Quantity, not a Q. I propose to replace them with methods that return a Q, so the type will be preserved. For example,
using Unitful
@eval Unitful begin
function *(x::Number, y::AbstractQuantity{T,D,U}) where {T,D,U}
y isa AffineQuantity && throw(AffineError("an invalid operation was attempted with affine quantities: $x*$y"))
Q = Base.typename(typeof(y)).wrapper
v = x*y.val
return Q{typeof(v), D, U}(v)
end
end
abstract type AbstractLength{T,D,U} <: Unitful.AbstractQuantity{T,D,U} end
abstract type AbstractRadius{T,D,U} <: AbstractLength{T,D,U} end
struct CircleRadius{T,D,U} <: AbstractRadius{T,D,U}
val::T
end
cr = CircleRadius{Float64, Unitful.𝐋, Unitful.FreeUnits{(u"m",), Unitful.𝐋, nothing}}(3.0)
@assert (4 * cr) isa CircleRadius
Related work
The mp-units C++ library supports a bunch of kinds for dimesionless quantities. I haven't got my head all the way around this one yet.
Questions
There are a lot of different cases in quantities.jl, * / // fma etc. Which ones are supposed to propagate Q and which ones are supposed to promote to Quantity? I'm not sure. Maybe mp-units has the answer.