Skip to content

Commit d36e875

Browse files
authored
Merge pull request #24 from SymbolicML/abstract-types-2
Make `AbstractQuantity` and `AbstractDimensions`
2 parents fb8f83b + a365da0 commit d36e875

File tree

12 files changed

+390
-241
lines changed

12 files changed

+390
-241
lines changed

.github/workflows/CI.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ jobs:
3333
- uses: julia-actions/cache@v1
3434
- uses: julia-actions/julia-buildpkg@v1
3535
- name: "Run tests"
36-
shell: bash
3736
run: |
3837
julia --color=yes --project=. -e 'import Pkg; Pkg.add("Coverage")'
3938
julia --color=yes --threads=auto --check-bounds=yes --depwarn=yes --code-coverage=user --project=. -e 'import Pkg; Pkg.test(coverage=true)'
39+
DQ_TEST_UPREFERRED=true julia --color=yes --threads=auto --check-bounds=yes --depwarn=yes --code-coverage=user --project=. -e 'import Pkg; Pkg.test(coverage=true)'
4040
julia --color=yes --project=. coverage.jl
4141
- name: "Coveralls"
4242
uses: coverallsapp/github-action@v2

Project.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ version = "0.4.0"
55

66
[deps]
77
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
8+
Tricks = "410a4b4d-49e4-4fbc-ab6d-cb71b17b3775"
89

910
[weakdeps]
1011
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"
@@ -19,8 +20,8 @@ julia = "1.6"
1920

2021
[extras]
2122
Ratios = "c84ed2f1-dad5-54f0-aa8e-dbefe2724439"
22-
SaferIntegers = "88634af6-177f-5301-88b8-7819386cfa38"
2323
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
24+
SaferIntegers = "88634af6-177f-5301-88b8-7819386cfa38"
2425
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
2526
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"
2627

ext/DynamicQuantitiesUnitfulExt.jl

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,28 @@ else
1010
import ..Unitful: @u_str
1111
end
1212

13-
# This lets the user override the preferred units:
14-
function unitful_equivalences()
15-
si_units = (length=u"m", mass=u"kg", time=u"s", current=u"A", temperature=u"K", luminosity=u"cd", amount=u"mol")
13+
function get_si_units()
14+
return (length=u"m", mass=u"kg", time=u"s", current=u"A", temperature=u"K", luminosity=u"cd", amount=u"mol")
15+
end
16+
17+
function validate_upreferred()
18+
si_units = get_si_units()
1619
for k in keys(si_units)
1720
if Unitful.upreferred(si_units[k]) !== si_units[k]
1821
error("Found custom `Unitful.preferunits`. This is not supported when interfacing Unitful and DynamicQuantities: you must leave the default `Unitful.upreferred`, which is the SI base units.")
1922
end
2023
end
24+
return true
25+
end
26+
27+
function unitful_equivalences()
28+
si_units = get_si_units()
2129
return NamedTuple((k => si_units[k] for k in keys(si_units)))
2230
end
2331

2432
Base.convert(::Type{Unitful.Quantity}, x::DynamicQuantities.Quantity) =
2533
let
34+
validate_upreferred()
2635
cumulator = DynamicQuantities.ustrip(x)
2736
dims = DynamicQuantities.dimension(x)
2837
equiv = unitful_equivalences()
@@ -35,16 +44,17 @@ Base.convert(::Type{Unitful.Quantity}, x::DynamicQuantities.Quantity) =
3544
end
3645

3746
Base.convert(::Type{DynamicQuantities.Quantity}, x::Unitful.Quantity{T}) where {T} = convert(DynamicQuantities.Quantity{T,DynamicQuantities.DEFAULT_DIM_TYPE}, x)
38-
Base.convert(::Type{DynamicQuantities.Quantity{T,R}}, x::Unitful.Quantity) where {T,R} =
47+
Base.convert(::Type{DynamicQuantities.Quantity{T,D}}, x::Unitful.Quantity) where {T,R,D<:DynamicQuantities.AbstractDimensions{R}} =
3948
let
4049
value = Unitful.ustrip(Unitful.upreferred(x))
41-
dimension = convert(DynamicQuantities.Dimensions{R}, Unitful.dimension(x))
50+
dimension = convert(D, Unitful.dimension(x))
4251
return DynamicQuantities.Quantity(convert(T, value), dimension)
4352
end
4453

45-
Base.convert(::Type{DynamicQuantities.Dimensions}, d::Unitful.Dimensions) = convert(DynamicQuantities.Dimensions{DynamicQuantities.DEFAULT_DIM_TYPE}, d)
54+
Base.convert(::Type{DynamicQuantities.Dimensions}, d::Unitful.Dimensions) = convert(DynamicQuantities.DEFAULT_DIM_TYPE, d)
4655
Base.convert(::Type{DynamicQuantities.Dimensions{R}}, d::Unitful.Dimensions{D}) where {R,D} =
4756
let
57+
validate_upreferred()
4858
cumulator = DynamicQuantities.Dimensions{R}()
4959
for dim in D
5060
dim_symbol = _map_dim_name_to_dynamic_units(typeof(dim))

src/DynamicQuantities.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module DynamicQuantities
22

3+
export AbstractQuantity, AbstractDimensions
34
export Quantity, Dimensions, DimensionError, ustrip, dimension, valid
45
export ulength, umass, utime, ucurrent, utemperature, uluminosity, uamount
56
export uparse, @u_str

src/fixed_rational.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ Base.convert(::Type{F}, x::Rational) where {F<:FixedRational} = F(x)
4747
Base.convert(::Type{Rational{R}}, x::F) where {R,F<:FixedRational} = Rational{R}(x.num, denom(F))
4848
Base.convert(::Type{Rational}, x::F) where {F<:FixedRational} = Rational{eltype(F)}(x.num, denom(F))
4949
Base.convert(::Type{AF}, x::F) where {AF<:AbstractFloat,F<:FixedRational} = convert(AF, x.num) / convert(AF, denom(F))
50+
Base.convert(::Type{I}, x::F) where {I<:Integer,F<:FixedRational} = convert(I, convert(Rational, x))
5051
Base.round(::Type{T}, x::F) where {T,F<:FixedRational} = div(convert(T, x.num), convert(T, denom(F)), RoundNearest)
5152
Base.promote(x::Integer, y::F) where {F<:FixedRational} = (F(x), y)
5253
Base.promote(x::F, y::Integer) where {F<:FixedRational} = reverse(promote(y, x))

src/math.jl

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,46 @@
1-
Base.:*(l::Dimensions, r::Dimensions) = @map_dimensions(+, l, r)
2-
Base.:*(l::Quantity, r::Quantity) = Quantity(l.value * r.value, l.dimensions * r.dimensions)
3-
Base.:*(l::Quantity, r::Dimensions) = Quantity(l.value, l.dimensions * r)
4-
Base.:*(l::Dimensions, r::Quantity) = Quantity(r.value, l * r.dimensions)
5-
Base.:*(l::Quantity, r) = Quantity(l.value * r, l.dimensions)
6-
Base.:*(l, r::Quantity) = Quantity(l * r.value, r.dimensions)
7-
Base.:*(l::Dimensions, r) = Quantity(r, l)
8-
Base.:*(l, r::Dimensions) = Quantity(l, r)
1+
Base.:*(l::AbstractDimensions, r::AbstractDimensions) = map_dimensions(+, l, r)
2+
Base.:*(l::AbstractQuantity, r::AbstractQuantity) = new_quantity(typeof(l), ustrip(l) * ustrip(r), dimension(l) * dimension(r))
3+
Base.:*(l::AbstractQuantity, r::AbstractDimensions) = new_quantity(typeof(l), ustrip(l), dimension(l) * r)
4+
Base.:*(l::AbstractDimensions, r::AbstractQuantity) = new_quantity(typeof(r), ustrip(r), l * dimension(r))
5+
Base.:*(l::AbstractQuantity, r) = new_quantity(typeof(l), ustrip(l) * r, dimension(l))
6+
Base.:*(l, r::AbstractQuantity) = new_quantity(typeof(r), l * ustrip(r), dimension(r))
7+
Base.:*(l::AbstractDimensions, r) = error("Please use an `AbstractQuantity` for multiplication. You used multiplication on types: $(typeof(l)) and $(typeof(r)).")
8+
Base.:*(l, r::AbstractDimensions) = error("Please use an `AbstractQuantity` for multiplication. You used multiplication on types: $(typeof(l)) and $(typeof(r)).")
99

10-
Base.:/(l::Dimensions, r::Dimensions) = @map_dimensions(-, l, r)
11-
Base.:/(l::Quantity, r::Quantity) = Quantity(l.value / r.value, l.dimensions / r.dimensions)
12-
Base.:/(l::Quantity, r::Dimensions) = Quantity(l.value, l.dimensions / r)
13-
Base.:/(l::Dimensions, r::Quantity) = Quantity(inv(r.value), l / r.dimensions)
14-
Base.:/(l::Quantity, r) = Quantity(l.value / r, l.dimensions)
15-
Base.:/(l, r::Quantity) = l * inv(r)
16-
Base.:/(l::Dimensions, r) = Quantity(inv(r), l)
17-
Base.:/(l, r::Dimensions) = Quantity(l, inv(r))
10+
Base.:/(l::AbstractDimensions, r::AbstractDimensions) = map_dimensions(-, l, r)
11+
Base.:/(l::AbstractQuantity, r::AbstractQuantity) = new_quantity(typeof(l), ustrip(l) / ustrip(r), dimension(l) / dimension(r))
12+
Base.:/(l::AbstractQuantity, r::AbstractDimensions) = new_quantity(typeof(l), ustrip(l), dimension(l) / r)
13+
Base.:/(l::AbstractDimensions, r::AbstractQuantity) = new_quantity(typeof(r), inv(ustrip(r)), l / dimension(r))
14+
Base.:/(l::AbstractQuantity, r) = new_quantity(typeof(l), ustrip(l) / r, dimension(l))
15+
Base.:/(l, r::AbstractQuantity) = l * inv(r)
16+
Base.:/(l::AbstractDimensions, r) = error("Please use an `AbstractQuantity` for division. You used division on types: $(typeof(l)) and $(typeof(r)).")
17+
Base.:/(l, r::AbstractDimensions) = error("Please use an `AbstractQuantity` for division. You used division on types: $(typeof(l)) and $(typeof(r)).")
1818

19-
Base.:+(l::Quantity, r::Quantity) = dimension(l) == dimension(r) ? Quantity(l.value + r.value, l.dimensions) : throw(DimensionError(l, r))
20-
Base.:-(l::Quantity) = Quantity(-l.value, l.dimensions)
21-
Base.:-(l::Quantity, r::Quantity) = l + (-r)
19+
Base.:+(l::AbstractQuantity, r::AbstractQuantity) = dimension(l) == dimension(r) ? new_quantity(typeof(l), ustrip(l) + ustrip(r), dimension(l)) : throw(DimensionError(l, r))
20+
Base.:-(l::AbstractQuantity) = new_quantity(typeof(l), -ustrip(l), dimension(l))
21+
Base.:-(l::AbstractQuantity, r::AbstractQuantity) = l + (-r)
2222

23-
Base.:+(l::Quantity, r) = dimension(l) == dimension(r) ? Quantity(l.value + r, l.dimensions) : throw(DimensionError(l, r))
24-
Base.:+(l, r::Quantity) = dimension(l) == dimension(r) ? Quantity(l + r.value, r.dimensions) : throw(DimensionError(l, r))
25-
Base.:-(l::Quantity, r) = l + (-r)
26-
Base.:-(l, r::Quantity) = l + (-r)
23+
Base.:+(l::AbstractQuantity, r) = iszero(dimension(l)) ? new_quantity(typeof(l), ustrip(l) + r, dimension(l)) : throw(DimensionError(l, r))
24+
Base.:+(l, r::AbstractQuantity) = iszero(dimension(r)) ? new_quantity(typeof(r), l + ustrip(r), dimension(r)) : throw(DimensionError(l, r))
25+
Base.:-(l::AbstractQuantity, r) = l + (-r)
26+
Base.:-(l, r::AbstractQuantity) = l + (-r)
2727

28-
_pow(l::Dimensions, r) = @map_dimensions(Base.Fix1(*, r), l)
29-
_pow(l::Quantity{T}, r) where {T} = Quantity(l.value^r, _pow(l.dimensions, r))
30-
_pow_as_T(l::Quantity{T}, r) where {T} = Quantity(l.value^convert(T, r), _pow(l.dimensions, r))
31-
Base.:^(l::Dimensions{R}, r::Integer) where {R} = _pow(l, r)
32-
Base.:^(l::Dimensions{R}, r::Number) where {R} = _pow(l, tryrationalize(R, r))
33-
Base.:^(l::Quantity{T,R}, r::Integer) where {T,R} = _pow(l, r)
34-
Base.:^(l::Quantity{T,R}, r::Number) where {T,R} = _pow_as_T(l, tryrationalize(R, r))
28+
# We don't promote on the dimension types:
29+
_pow(l::AbstractDimensions{R}, r::R) where {R} = map_dimensions(Base.Fix1(*, r), l)
30+
Base.:^(l::AbstractDimensions{R}, r::Number) where {R} = _pow(l, tryrationalize(R, r))
31+
Base.:^(l::AbstractQuantity{T,D}, r::Integer) where {T,R,D<:AbstractDimensions{R}} = new_quantity(typeof(l), ustrip(l)^r, dimension(l)^r)
32+
Base.:^(l::AbstractQuantity{T,D}, r::Number) where {T,R,D<:AbstractDimensions{R}} =
33+
let dim_pow = tryrationalize(R, r), val_pow = convert(T, dim_pow)
34+
# Need to ensure we take the numerical power by the rationalized quantity:
35+
return new_quantity(typeof(l), ustrip(l)^val_pow, dimension(l)^dim_pow)
36+
end
3537

36-
Base.inv(d::Dimensions) = @map_dimensions(-, d)
37-
Base.inv(q::Quantity) = Quantity(inv(q.value), inv(q.dimensions))
38+
Base.inv(d::AbstractDimensions) = map_dimensions(-, d)
39+
Base.inv(q::AbstractQuantity) = new_quantity(typeof(q), inv(ustrip(q)), inv(dimension(q)))
3840

39-
Base.sqrt(d::Dimensions{R}) where {R} = d^inv(convert(R, 2))
40-
Base.sqrt(q::Quantity) = Quantity(sqrt(q.value), sqrt(q.dimensions))
41-
Base.cbrt(d::Dimensions{R}) where {R} = d^inv(convert(R, 3))
42-
Base.cbrt(q::Quantity) = Quantity(cbrt(q.value), cbrt(q.dimensions))
41+
Base.sqrt(d::AbstractDimensions{R}) where {R} = d^inv(convert(R, 2))
42+
Base.sqrt(q::AbstractQuantity) = new_quantity(typeof(q), sqrt(ustrip(q)), sqrt(dimension(q)))
43+
Base.cbrt(d::AbstractDimensions{R}) where {R} = d^inv(convert(R, 3))
44+
Base.cbrt(q::AbstractQuantity) = new_quantity(typeof(q), cbrt(ustrip(q)), cbrt(dimension(q)))
4345

44-
Base.abs(q::Quantity) = Quantity(abs(q.value), q.dimensions)
46+
Base.abs(q::AbstractQuantity) = new_quantity(typeof(q), abs(ustrip(q)), dimension(q))

src/types.jl

Lines changed: 43 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
const DEFAULT_DIM_TYPE = FixedRational{Int32, 2^4 * 3^2 * 5^2 * 7}
1+
import Tricks: static_fieldnames, static_fieldtypes
2+
3+
const DEFAULT_DIM_BASE_TYPE = FixedRational{Int32,2^4 * 3^2 * 5^2 * 7}
24
const DEFAULT_VALUE_TYPE = Float64
35

6+
abstract type AbstractQuantity{T,D} end
7+
abstract type AbstractDimensions{R} end
8+
49
"""
510
Dimensions{R}
611
@@ -22,54 +27,33 @@ which is by default a rational number.
2227
2328
# Constructors
2429
25-
- `Dimensions(args...)`: Pass all the dimensions as arguments. `R` is set to `DEFAULT_DIM_TYPE`.
26-
- `Dimensions(; kws...)`: Pass a subset of dimensions as keyword arguments. `R` is set to `DEFAULT_DIM_TYPE`.
30+
- `Dimensions(args...)`: Pass all the dimensions as arguments. `R` is set to `DEFAULT_DIM_BASE_TYPE`.
31+
- `Dimensions(; kws...)`: Pass a subset of dimensions as keyword arguments. `R` is set to `DEFAULT_DIM_BASE_TYPE`.
2732
- `Dimensions(::Type{R}; kws...)` or `Dimensions{R}(; kws...)`: Pass a subset of dimensions as keyword arguments, with the output type set to `Dimensions{R}`.
28-
- `Dimensions{R}(args...)`: Pass all the dimensions as arguments, with the output type set to `Dimensions{R}`.
33+
- `Dimensions{R}()`: Create a dimensionless object typed as `Dimensions{R}`.
2934
- `Dimensions{R}(d::Dimensions)`: Copy the dimensions from another `Dimensions` object, with the output type set to `Dimensions{R}`.
30-
3135
"""
32-
struct Dimensions{R <: Real}
36+
struct Dimensions{R<:Real} <: AbstractDimensions{R}
3337
length::R
3438
mass::R
3539
time::R
3640
current::R
3741
temperature::R
3842
luminosity::R
3943
amount::R
40-
41-
function Dimensions(length::_R,
42-
mass::_R,
43-
time::_R,
44-
current::_R,
45-
temperature::_R,
46-
luminosity::_R,
47-
amount::_R) where {_R<:Real}
48-
new{_R}(length, mass, time, current, temperature, luminosity, amount)
49-
end
50-
Dimensions(; kws...) = Dimensions(DEFAULT_DIM_TYPE; kws...)
51-
Dimensions(::Type{_R}; kws...) where {_R} = Dimensions(
52-
tryrationalize(_R, get(kws, :length, zero(_R))),
53-
tryrationalize(_R, get(kws, :mass, zero(_R))),
54-
tryrationalize(_R, get(kws, :time, zero(_R))),
55-
tryrationalize(_R, get(kws, :current, zero(_R))),
56-
tryrationalize(_R, get(kws, :temperature, zero(_R))),
57-
tryrationalize(_R, get(kws, :luminosity, zero(_R))),
58-
tryrationalize(_R, get(kws, :amount, zero(_R))),
59-
)
60-
Dimensions{_R}(; kws...) where {_R} = Dimensions(_R; kws...)
61-
Dimensions{_R}(args...) where {_R} = Dimensions(Base.Fix1(convert, _R).(args)...)
62-
Dimensions{_R}(d::Dimensions) where {_R} = Dimensions{_R}(d.length, d.mass, d.time, d.current, d.temperature, d.luminosity, d.amount)
6344
end
6445

65-
const DIMENSION_NAMES = Base.fieldnames(Dimensions)
66-
const DIMENSION_SYNONYMS = (:m, :kg, :s, :A, :K, :cd, :mol)
67-
const SYNONYM_MAPPING = NamedTuple(DIMENSION_NAMES .=> DIMENSION_SYNONYMS)
46+
(::Type{D})(::Type{R}; kws...) where {R,D<:AbstractDimensions} = constructor_of(D){R}((tryrationalize(R, get(kws, k, zero(R))) for k in static_fieldnames(D))...)
47+
(::Type{D})(; kws...) where {R,D<:AbstractDimensions{R}} = constructor_of(D)(R; kws...)
48+
(::Type{D})(; kws...) where {D<:AbstractDimensions} = D(DEFAULT_DIM_BASE_TYPE; kws...)
49+
(::Type{D})(d::AbstractDimensions) where {R,D<:AbstractDimensions{R}} = D((getproperty(d, k) for k in static_fieldnames(D))...)
50+
51+
const DEFAULT_DIM_TYPE = Dimensions{DEFAULT_DIM_BASE_TYPE}
6852

6953
"""
70-
Quantity{T,R}
54+
Quantity{T,D}
7155
72-
Physical quantity with value `value` of type `T` and dimensions `dimensions` of type `Dimensions{R}`.
56+
Physical quantity with value `value` of type `T` and dimensions `dimensions` of type `D`.
7357
For example, the velocity of an object with mass 1 kg and velocity
7458
2 m/s is `Quantity(2, mass=1, length=1, time=-1)`.
7559
You should access these fields with `ustrip(q)`, and `dimensions(q)`.
@@ -83,26 +67,37 @@ dimensions according to the operation.
8367
# Fields
8468
8569
- `value::T`: value of the quantity of some type `T`. Access with `ustrip(::Quantity)`
86-
- `dimensions::Dimensions{R}`: dimensions of the quantity with dimension type `R`. Access with `dimension(::Quantity)`
70+
- `dimensions::D`: dimensions of the quantity. Access with `dimension(::Quantity)`
8771
8872
# Constructors
8973
90-
- `Quantity(x; kws...)`: Construct a quantity with value `x` and dimensions given by the keyword arguments. The value type is inferred from `x`. `R` is set to `DEFAULT_DIM_TYPE`.
91-
- `Quantity(x, ::Type{R}; kws...)`: Construct a quantity with value `x`. The dimensions parametric type is set to `R`.
92-
- `Quantity(x, d::Dimensions{R})`: Construct a quantity with value `x` and dimensions `d`.
93-
- `Quantity{T}(q::Quantity)`: Construct a quantity with value `q.value` and dimensions `q.dimensions`, but with value type converted to `T`.
94-
- `Quantity{T,R}(q::Quantity)`: Construct a quantity with value `q.value` and dimensions `q.dimensions`, but with value type converted to `T` and dimensions parametric type set to `R`.
74+
- `Quantity(x; kws...)`: Construct a quantity with value `x` and dimensions given by the keyword arguments. The value
75+
type is inferred from `x`. `R` is set to `DEFAULT_DIM_TYPE`.
76+
- `Quantity(x, ::Type{D}; kws...)`: Construct a quantity with value `x` with dimensions given by the keyword arguments,
77+
and the dimensions type set to `D`.
78+
- `Quantity(x, d::D)`: Construct a quantity with value `x` and dimensions `d` of type `D`.
79+
- `Quantity{T}(...)`: As above, but converting the value to type `T`. You may also pass a `Quantity` as input.
80+
- `Quantity{T,D}(...)`: As above, but converting the value to type `T` and dimensions to `D`. You may also pass a
81+
`Quantity` as input.
9582
"""
96-
struct Quantity{T, R}
83+
struct Quantity{T,D<:AbstractDimensions} <: AbstractQuantity{T,D}
9784
value::T
98-
dimensions::Dimensions{R}
99-
100-
Quantity(x; kws...) = new{typeof(x), DEFAULT_DIM_TYPE}(x, Dimensions(; kws...))
101-
Quantity(x, ::Type{_R}; kws...) where {_R} = new{typeof(x), _R}(x, Dimensions(_R; kws...))
102-
Quantity(x, d::Dimensions{_R}) where {_R} = new{typeof(x), _R}(x, d)
103-
Quantity{T}(q::Quantity) where {T} = Quantity(convert(T, q.value), dimension(q))
104-
Quantity{T,R}(q::Quantity) where {T,R} = Quantity(convert(T, q.value), Dimensions{R}(dimension(q)))
85+
dimensions::D
10586
end
87+
(::Type{Q})(x::T, ::Type{D}; kws...) where {D<:AbstractDimensions,T,T2,Q<:AbstractQuantity{T2}} = constructor_of(Q)(convert(T2, x), D(; kws...))
88+
(::Type{Q})(x, ::Type{D}; kws...) where {D<:AbstractDimensions,Q<:AbstractQuantity} = constructor_of(Q)(x, D(; kws...))
89+
(::Type{Q})(x::T; kws...) where {T,T2,Q<:AbstractQuantity{T2}} = constructor_of(Q)(convert(T2, x), dim_type(Q)(; kws...))
90+
(::Type{Q})(x; kws...) where {Q<:AbstractQuantity} = constructor_of(Q)(x, dim_type(Q)(; kws...))
91+
92+
(::Type{Q})(q::AbstractQuantity) where {T,D<:AbstractDimensions,Q<:AbstractQuantity{T,D}} = constructor_of(Q)(convert(T, ustrip(q)), convert(D, dimension(q)))
93+
(::Type{Q})(q::AbstractQuantity) where {T,Q<:AbstractQuantity{T}} = constructor_of(Q)(convert(T, ustrip(q)), dimension(q))
94+
95+
new_dimensions(::Type{D}, dims...) where {D<:AbstractDimensions} = constructor_of(D)(dims...)
96+
new_quantity(::Type{Q}, l, r) where {Q<:AbstractQuantity} = constructor_of(Q)(l, r)
97+
98+
dim_type(::Type{Q}) where {T,D<:AbstractDimensions,Q<:AbstractQuantity{T,D}} = D
99+
dim_type(::Type{<:AbstractQuantity}) = DEFAULT_DIM_TYPE
100+
constructor_of(::Type{T}) where {T} = Base.typename(T).wrapper
106101

107102
struct DimensionError{Q1,Q2} <: Exception
108103
q1::Q1

0 commit comments

Comments
 (0)