A very common operation when trying to write package which works with unitful quantities is getting the unitless element-type. This is required to find the right eps
or set the tolerance to the correct Number
type. However, I am not sure of a completely generic way to handle this. typeof(first(one(u)))
works well in a pinch, but I am noticing an issue with GPUArray
s which do not have indexing and thus don't have first
defined. So the only way I know of to always get the unitless element type is eltype(u./u)
, which has an extra operation just to grab the type.
Is there a generic function to strip the units off of a type? Some unitless(T)
to get whatever the type of the non-unit part is? That would be a clear way to implement this.
Even eltype(u./u)
doesn't work:
julia> using Unitful
julia> x = 3u"m"
3 m
julia> x/x
1.0
which is Float64
rather than Int
.
Unitful has ustrip
. If you're asking about supporting multiple unit frameworks, then this doesn't seem to be a Julia issue, it's more something for the package ecosystem to agree on. There could be a UnitStubs.jl
package that all of them import
and extend; the stubs package would contain only the fallbacks, like
ustrip(x::Number) = x
and then each package could specialize it for the types defined in the package.
Unitful has ustrip. If you're asking about supporting multiple unit frameworks, then this doesn't seem to be a Julia issue, it's more something for the package ecosystem to agree on. There could be a UnitStubs.jl package that all of them import and extend
Wouldn't this be useful in Base for the same reason as oneunit
? Yes, the reason for this is because getting all of the different unit packages to work is extremely difficult without this, and I am looking for a good way to not take on a dependency for each library. But, if something like this is useful for unitful types in Base like Dates, then that would be a good solution.
A kind of funny workaround for this is typeof(one(eltype(x)))
, since one
strips units unlike oneunit
. I'm not sure how universally applicable it is though.
Yes, if one
is properly implemented that should work. If it's not properly implemented, it should be fixed anyway...
If you want to go the UnitStubs
way, the main point is that JuliaDiffEq packages could just say using UnitStubs
and not need to know which package is being used by the user to input values.
I agree these are conceptually relevant for dates, but AFAICT all dates objects are fundamentally Int64
, so I'm not sure generic eltype manipulations are necessary. The main reason we added oneunit
is that people were misusing one
, conflating the additive generator with the multiplicative identity. I'm not certain further unit manipulations are really necessary? Not that I think it would be a big deal, though.
Yes, if one is properly implemented that should work. If it's not properly implemented, it should be fixed anyway...
Well if that's the case then I'm fine with using it. Would a simple
unitless_eltype(x) = typeof(one(eltype(x)))
be worthy of inclusion into Base?
I think quantity(x)
would be a good name for getting the numeric value without the unit. A generic definition could be quantity(x) = x*(one(x)/oneunit(x))
. Does not work well for integers though.
Would changing /
for 梅
work?
It might well do so. I guess as long as uniful quantities behave as expected wrt 梅
. Of course there's the issue that multiplying 1m * 1m^{-1}
will produce a 1*m^0
whereas what one wants is actually 1
without any unit 鈥撀爑nless I'm wrong and Unitful
handles this. Perhaps the best approach is just to introduce Base.quantity
which acts as the identity on pure numbers and allow unit packages to extend it by unwrapping unitful quantities and returning the wrapped value. I.e. don't try to provide a generic fallback.
unless I'm wrong and Unitful handles this.
Unitful actually does handle this:
using Unitful
1u"m"/1u"m"
# 1.00
Perhaps the best approach is just to introduce Base.quantity which acts as the identity on pure numbers and allow unit packages to extend it by unwrapping unitful quantities and returning the wrapped value. I.e. don't try to provide a generic fallback.
I agree with this because while Unitful special-cased it to work, there's no reason to assume each other units package does. A generic fallback is inherently brittle for this reason, so I agree that it should probably be left up to the units packages to handle the overload.
The usual nomenclature (e.g. mathematica) is that quantity refers to the unitful quantity, not the unitless one.
Nevertheless it's not strictly always possible to strip a unit, because that may be ambiguous or not make sense... taking Dates.CompoundPeriod
as an example. This comes up in practice with non-dates also, so as much code as possible should be written in a dimensionally-correct manner and avoid unit stripping. Of course, in cases where oneunit
can be defined, presumably the unit-stripping operation can also.
I think having quantity
(or whatever name is agreed on) along with oneunit
makes sense because it allows to formulate some nice invariants, e.g.
quantity(oneunit(x)) == one(x)
quantity(x) * oneunit(x) == x
My gut feeling is that these equalities should also hold when taking typeof
on both sides, but there might be cases where that cannot be made to work, not sure.
If quantity
typically means the opposite then maybe unitless
would be a better name?
How often does one need to get the value without the unit? The original question was about getting the unitless type, where typeof(one(x))
works just fine. But I guess if we have unitless(T)
we might as well have unitless(x)
as well.
I don't think we should have unitless_eltype(x)
, however, since I see no advantage of this vs. unitless(eltype(x))
.
unitless(x)
would be also a solution to the zeronounit
-question. Especially for arrays one(x)
does not work and typeof
loses the size
information, so unitless(x)
would be useful there.
Most helpful comment
The usual nomenclature (e.g. mathematica) is that quantity refers to the unitful quantity, not the unitless one.
Nevertheless it's not strictly always possible to strip a unit, because that may be ambiguous or not make sense... taking
Dates.CompoundPeriod
as an example. This comes up in practice with non-dates also, so as much code as possible should be written in a dimensionally-correct manner and avoid unit stripping. Of course, in cases whereoneunit
can be defined, presumably the unit-stripping operation can also.