Iβm developing some electromagnetic code and want to use Unitful.jl thoroughly to make sure that data structures and algorithms are physically consistent.
One feature I would like to have is the ability to quickly set up structures using a basic floating-point data type with the appropriate bitsize, e.g., Float32
vs Float64
. However, I struggle to understand how to make the Julia type system to do what I want.
I can explain the problem using a very simple 2D vector data structure as an example:
import Unitful: Length, m
struct Vec{T, L <: Length{T}}
x::L
y::L
end
This type works well with both Float32
and Float64
numbers:
julia> Vec(1.0m, 2.0m)
Vec{Float64, Quantity{Float64, π, Unitful.FreeUnits{(m,), π, nothing}}}(1.0 m, 2.0 m)
julia> Vec(1.0f0m, 2.0f0m)
Vec{Float32, Quantity{Float32, π, Unitful.FreeUnits{(m,), π, nothing}}}(1.0f0 m, 2.0f0 m)
Suppose that now I want to define an outer constructor where I pass the Float32
/Float64
/ComplexF32
/β¦ type explicitly, making sure that the constructor properly converts the types. This could be useful if e.g. I want to be sure to use a wide data type in some accuracy-sensitive algorithm.
In this particular example, I would like to write Vec{Float64}(x, y)
and keep the lengths x
and y
stored as Float64
lengths even if the values passed as x
and y
are Float32
.
I struggle to understand how to code it, as the following attempt does not work:
julia> Vec{T}(x::L, y::L) where {T, L <: Length} = Vec{T, L}(T(x), T(y))
julia> Vec{Float64}(1.0f0m, 2.0f0m)
ERROR: TypeError: in Vec, in L, expected L<:(Union{Quantity{Float64, π, U}, Unitful.Level{L, S, Quantity{Float64, π, U}} where {L, S}} where U), got Type{Quantity{Float32, π, Unitful.FreeUnits{(m,), π, nothing}}}
Stacktrace:
[1] (Vec{β¦} where L<:(Union{β¦} where U))(x::Quantity{β¦}, y::Quantity{β¦})
@ Main ./REPL[10]:1
[2] top-level scope
@ REPL[15]:1
Some type information was truncated. Use `show(err)` to see complete types.
I understand that the problem lies in the fact that when I write Vec{T, L}
, the type L
is still based on Float32
. But I fail to see an easy way to βpromoteβ type L
so that it keeps using the same measurement units (m
, ft
, inch
β¦) as the input parameters while switching the basic type from Float32
to Float64
.
Can anybody provide me a few hints, please?
Edit: Fix a typo.