Use unitful unit to a variable

How can I attach a unit to a variable ?

e.g.

julia> using Unitful

julia> 25u"km" #works
25 km

julia> a = 25; # define a variable

julia> #would expect something like that or similar to work
julia> au = @u_str(a, "km")
ERROR: LoadError: MethodError: no method matching var"@u_str"(::LineNumberNode, ::Module, ::Symbol, ::String)
Closest candidates are:
  var"@u_str"(::LineNumberNode, ::Module, ::Any) at ~/.julia/packages/Unitful/ApCuY/src/user.jl:629
in expression starting at REPL[6]:1

For now I solve this with eval, but it doesn’t feel right:

julia> au = eval(:a)u"km"
25 km

What’s the way to go here ?

julia> a = 25u"km"
25 km

julia> a
25 km

this?

Or this:

julia> b = 25 * oneunit(a)
25 km

In some sense, you cannot “attach” a unit to variable, because a variable with a unit is in some sense just a type of number. Is like if you could attach the property of being a Complex{Float64} to a Float64. You cannot, you have to create a new variable for that.

I want to take the value inside of a variable and attach it to a type.
I don’t want to use a literal like 25 again.
E.g., let’s say I derived programmatically a long Int[] array and I want to attach u"km" to it.
Can I do it without eval ?

julia> very_complex_func() = [1,2,3,4,5]
very_complex_func (generic function with 1 method)

julia> ar = very_complex_func();

julia> aru = eval(ar)u"km"
5-element Vector{Quantity{Int64, 𝐋, Unitful.FreeUnits{(km,), 𝐋, nothing}}}:
 1 km
 2 km
 3 km
 4 km
 5 km

I don’t quite agree.
I can initiate a Complex and a Float64 with a variable and not always a literal is needed.
Also I can “attach” a Float64 value to a Complex{Float64} like the following:

julia> num = 3;

julia> a = Float64(num);

julia> ac = Complex(a,a)
3.0 + 3.0im

Instead to initialize a Unitful type, it looks like I always need a literal.

You can multiply by the unit, for example. But I don’t know if this is what you don’t want to do.

julia> using Unitful
julia> a = [1, 2, 3]
julia> b = 1u"km".*a
3-element Vector{Quantity{Int64, 𝐋, Unitful.FreeUnits{(km,), 𝐋, nothing}}}:
 1 km
 2 km
 3 km

What you were trying to do with @u_str is also possible, but there is no need to introduce the a intro the macro call.

julia> a*@u_str("km")
3-element Vector{Quantity{Int64, 𝐋, Unitful.FreeUnits{(km,), 𝐋, nothing}}}:
 1 km
 2 km
 3 km

What happens is that the way of “attaching” a unit to a value, even in the example of a = 25u"km" is doing a product between 25 and the thing that comes back from u"km"

I don´t know if Unitful has a specific function for that. But you cannot “attach” the units to such array, because the array is of immutable values, and the unitful values are a different type of animal.

Thus, you can do, for example:

julia> x = [1,2,3,4,5];

julia> x .* 1u"km"
5-element Vector{Quantity{Int64, 𝐋, Unitful.FreeUnits{(km,), 𝐋, nothing}}}:
 1 km
 2 km
 3 km
 4 km
 5 km

I’m not sure if there is a convert(...) type of function in Unitful for doing that with another syntax. But anyway you need to define a new variable.

(I think that if the purpose is to propagate units that are attached to some input variable, the most useful pattern is something like:

julia> a = 1u"km"
1 km

julia> x = [1,2];

julia> y = x .* oneunit(a)
2-element Vector{Quantity{Int64, 𝐋, Unitful.FreeUnits{(km,), 𝐋, nothing}}}:
 1 km
 2 km

Yeah, the multiplication with 1u"km" again works (probably better than the eval) but again it seems to me like a “hack”. Anyway, I better get used to it. :smiley: Thanks!

It is not a hack, it is actually the usual way:

You can see it with:

julia> @which 25u"km"
*(x::Number, y::Unitful.Units, z::Unitful.Units...) in Unitful at /Users/arreyes/.julia/packages/Unitful/ApCuY/src/quantities.jl:26

This actually calls the product of 25 and u"km". This product is defined at unitful. So it is not more esoteric than that.

If you think in terms of actual units, it is not a hack, it is what actually make sense. A dimensionless quantity does not assume dimensions, but it may be multiplied by a quantity with dimensions, and the result has the dimensions of that quantity.

It is not that you can “fix” the fact that you forgot to write the units of a quantity, because the types are different. For instance, you cannot do this:

julia> x = [ 1, 2 ];

julia> x[1] = 1u"km"
ERROR: DimensionError:  and km are not dimensionally compatible.

Because x is a container of unitless (Int64) numbers only.

You can, however, do this:

julia> x = [1.0, 2.0]u"km"
2-element Vector{Quantity{Float64, 𝐋, Unitful.FreeUnits{(km,), 𝐋, nothing}}}:
 1.0 km
 2.0 km

julia> x[1] = 1.0u"m"
1.0 m

julia> x
2-element Vector{Quantity{Float64, 𝐋, Unitful.FreeUnits{(km,), 𝐋, nothing}}}:
 0.001 km
   2.0 km

since how it makes sense to convert the meter to the km unit which the vector carries.

*you could do this:

julia> x = Number[1.0, 2.0];

julia> x[1] = 1.0u"km"
1.0 km

julia> x
2-element Vector{Number}:
 1.0 km
      2.0

but that is only because that vector can contain anything that is a subtype of number, and there is no relation between the values before and after the mutation - and this is very bad for performance.