Hello everyone;
I have been getting the hang of the very nice Unitful.jl
package recently. I really like it, and would like to take the chance to thank those in the community who develop(ed) and maintain(ed) this package!
I thought it might be worth sharing a two observations that came up while I’ve been learning (so far); perhaps it will help someone else in the future.
Unitful.jl
: document the difference betweenAbstractType
and@derived_dimension
sametype()
error when comparingUnitful.jl
values
Please note; neither observation means I cannot use Unitful.jl
happily or productively. Rather I hope just that these things are just nice-to-know and/or good-to-have-on-record.
Thanks for reading and have a nice day!
~~
P.S. I’m an engineer by training, so please consider me essentially a computer science novice. I’m happy to help try to ‘improve’ things in the package, (e.g. documentation?) if I am able to be helpful in this way, but I am not currently sure how I would even start with such a thing or who I should speak with.
Unitful.jl
: document the difference between AbstractType
and @derived_dimension
In the Unitful.jl documentation section called ‘Dimensions in a type definition’. To me it is saying that Unitful.Length
and Unitful.Mass
are abstract types:
It may be tempting to specify the dimensions of a quantity in a type definition, e.g.
struct Person height::Unitful.Length mass::Unitful.Mass end
However, these are abstract types. If performance is important, it may be better just to pick a concrete
Quantity
type.
As such, this result seems to be contradictory (to me):
julia> Base.isabstracttype(Unitful.Length)
false
julia> Base.isabstracttype(Unitful.Mass)
false
Even though these are behaving (mostly) like I would expect from AbstractTypes:
julia> typeof(1u"m") <: Unitful.Length
true
julia> typeof(u"m") <: Unitful.Length
false
From what I can see within Unitful.jl
’s source code, specifically within pkgdefaults.jl
’s code, these are @derived_dimension
not abstract types.
There are four abstract types defined (which I could find within Types.jl
’s code), which are recognised by Base.isabstracttype()
:
Unitful.Unitlike
,Unitful.Units
,Unitful.AbstractQuantity
andUnitful.LogScaled
Questions/Suggestions
Perhaps one of the following suggestions could be considered for future updates to the Unitful.jl
package?
Unitful.jl
’s documentation could be updated to clarify that they behave like abstract types, but will not returntrue
when tested usingBase.isabstracttype
?Unitful.jl
’s code could updated such that the ‘abstract types’ which are@derived_dimension
such asUnitful.Length
andUnitful.Mass
so that they returntrue
when tested usingBase.isabstracttype
?
sametype()
error when comparing Unitful.jl
values
The minimum working example included below reveals an error when passing Unitful.jl
values, for example stored in a Vector
into another function.
The error is ultimately thrown by the function sametype_error()
(line 381 within Promotion.jl
within Module Base
) and I ask you kindly to run the MWE below to see the full stack trace for the error.
Details for reproducing the error
The error seems to (me) to appear only when:
-
A vector of values with Unitful units, which will be passed to another function, is created with this syntax:
Quantity{Number}[1*u"m"]
whereNumber
is any numericalAbstractType
except forAbstractFloat
, which seems to work fine. -
The function to which the values are passed includes a conditional comparison (e.g.
≤
) involving the value passed to it.
Note that if the vector is created with an improved syntax such as simply [1*u"m"]
then this error is no longer revealed.
I mention it here primarily in case:
- anyone else has seen this error and is wondering how to avoid it; and
- in case it has consequences which are beyond my understanding for the Unitful.jl package as a whole.
# Minimum Working Example 'MWE' - should run in REPL directly
using Unitful
# Functions to reveal error and test different numerical types
function unitful_double(Q̇::Unitful.VolumeFlow) :: Unitful.VolumeFlow
return 2Q̇
end
function unitful_conditional_double(Q̇::Unitful.VolumeFlow) :: Unitful.VolumeFlow
Q̇ ≤ 10u"L/minute" && return 2Q̇
end
function tests(value)
println("The type of the input for this test is: \n",
" - $(typeof(value))\n",
)
try
unitful_double(value)
println("✔ Success: 'unitful_double()' worked for input with type:\n",
" - $(typeof(value))\n",
)
catch
@warn string("❌ Failure: error thrown by 'unitful_double()' with input type:\n",
" - $(typeof(value))\n",
)
end
try
unitful_conditional_double(value)
println("✔ Success: 'unitful_conditional_double()' worked for input with type:\n",
" - $(typeof(value))\n",
)
catch
@warn string("❌ Failure: error thrown by 'unitful_conditional_double()' with input type:\n",
" - $(typeof(value))\n",
)
end
end
### Test different numerical types, both non-abstract and abstract ###
## Non-abstract numerical types ##
# Float64 #
isabstracttype(Float64)
val_from_vector_FLOAT64 = first(Quantity{Float64}[1*u"L/minute"])
tests(val_from_vector_FLOAT64)
# Int64 #
isabstracttype(Int64)
val_from_vector_INT64 = first(Quantity{Int64}[1*u"L/minute"])
tests(val_from_vector_INT64)
## Abstract numerical types ##
# AbstractFloat #
isabstracttype(AbstractFloat)
val_from_vector_ABSTRACTFLOAT = first(Quantity{AbstractFloat}[1*u"L/minute"])
tests(val_from_vector_ABSTRACTFLOAT)
# Integer #
isabstracttype(Integer)
val_from_vector_INTEGER = first(Quantity{Integer}[1*u"L/minute"])
tests(val_from_vector_INTEGER)
# Real #
isabstracttype(Real)
val_from_vector_REAL = first(Quantity{Real}[1*u"L/minute"])
tests(val_from_vector_REAL)
# Number #
isabstracttype(Number)
val_from_vector_NUMBER = first(Quantity{Number}[1*u"L/minute"])
tests(val_from_vector_NUMBER)
## Print full error to REPL
unitful_conditional_double(val_from_vector_NUMBER)
Julia version information:
julia> versioninfo()
Julia Version 1.8.0
Commit 5544a0fab76 (2022-08-17 13:38 UTC)
Platform Info:
OS: macOS (x86_64-apple-darwin21.4.0)
CPU: 4 × Intel(R) Core(TM) i5-5250U CPU @ 1.60GHz
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-13.0.1 (ORCJIT, broadwell)
Threads: 1 on 2 virtual cores
Environment:
JULIA_EDITOR = code
JULIA_NUM_THREADS =
Unitful.jl version information:
(@v1.8) pkg> st Unitful
Status `~/.julia/environments/v1.8/Project.toml`
[1986cc42] Unitful v1.11.0