diff --git a/src/DynamicQuantities.jl b/src/DynamicQuantities.jl index 08d06121..4bd9bd08 100644 --- a/src/DynamicQuantities.jl +++ b/src/DynamicQuantities.jl @@ -11,6 +11,7 @@ include("internal_utils.jl") include("fixed_rational.jl") include("types.jl") include("utils.jl") +include("wildcard.jl") include("math.jl") include("arrays.jl") include("units.jl") diff --git a/src/math.jl b/src/math.jl index 6185b14e..18da2e90 100644 --- a/src/math.jl +++ b/src/math.jl @@ -25,7 +25,7 @@ for (type, base_type, _) in ABSTRACT_QUANTITY_TYPES, op in (:+, :-) @eval begin function Base.$op(l::$type, r::$type) dimension(l) == dimension(r) || throw(DimensionError(l, r)) - return new_quantity(typeof(l), $op(ustrip(l), ustrip(r)), dimension(l)) + return new_quantity(typeof(l), $op(ustrip(l), ustrip(r)), combine_dimension_with_wildcard(l, r)) end function Base.$op(l::$type, r::$base_type) iszero(dimension(l)) || throw(DimensionError(l, r)) diff --git a/src/utils.jl b/src/utils.jl index b312a9a0..a1368948 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -135,7 +135,6 @@ Base.one(::D) where {D<:AbstractDimensions} = one(D) # Additive identities (zero) Base.zero(q::Q) where {Q<:UnionAbstractQuantity} = new_quantity(Q, zero(ustrip(q)), dimension(q)) Base.zero(::AbstractDimensions) = error("There is no such thing as an additive identity for a `AbstractDimensions` object, as + is only defined for `UnionAbstractQuantity`.") -Base.zero(::Type{<:UnionAbstractQuantity}) = error("Cannot create an additive identity for a `UnionAbstractQuantity` type, as the dimensions are unknown. Please use `zero(::UnionAbstractQuantity)` instead.") Base.zero(::Type{<:AbstractDimensions}) = error("There is no such thing as an additive identity for a `AbstractDimensions` type, as + is only defined for `UnionAbstractQuantity`.") # Dimensionful 1 (oneunit) diff --git a/src/wildcard.jl b/src/wildcard.jl new file mode 100644 index 00000000..123820d4 --- /dev/null +++ b/src/wildcard.jl @@ -0,0 +1,29 @@ +struct WildcardDimensions{R} <: AbstractDimensions{R} + # We simply choose Bool since it will get promoted + WildcardDimensions() = new{Bool}() +end + +constructorof(::Type{<:WildcardDimensions}) = WildcardDimensions +dimension_names(::Type{<:WildcardDimensions}) = () + +# Wildcard dimensions are always compatible: +Base.:(==)(::AbstractDimensions, ::WildcardDimensions) = true +Base.:(==)(::WildcardDimensions, ::AbstractDimensions) = true + +# For any zero/oneunit specified on the type, we return a wildcard dimension instead: +Base.zero(::Type{Q}) where {T,Q<:UnionAbstractQuantity{T}} = constructorof(Q)(zero(T), WildcardDimensions()) + +# For propagation rules, we assume all relevant dimensions are zero +Base.getproperty(::WildcardDimensions, ::Symbol) = zero(Bool) +Base.iszero(::WildcardDimensions) = true + +# Always promote to the other dimension type: +Base.promote_rule(::Type{D}, ::Type{<:WildcardDimensions}) where {D<:AbstractDimensions} = D + +# Other utility rules: +Base.show(io::IO, ::WildcardDimensions) = print(io, "[*]") + +# + and - will remove wildcard dimensions: +combine_dimension_with_wildcard(q1::UnionAbstractQuantity, q2::UnionAbstractQuantity) = combine_dimension_with_wildcard(dimension(q1), dimension(q2)) +combine_dimension_with_wildcard(d::AbstractDimensions, ::AbstractDimensions) = d +combine_dimension_with_wildcard(::WildcardDimensions, d::AbstractDimensions) = d