Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Unitful.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import LinearAlgebra: istril, istriu, norm
import Random

export logunit, unit, absoluteunit, dimension, uconvert, ustrip, upreferred
export WithUnits, WithDims
export @dimension, @derived_dimension, @refunit, @unit, @affineunit, @u_str
export Quantity, DimensionlessQuantity, NoUnits, NoDims

Expand Down
43 changes: 43 additions & 0 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,49 @@ struct DimensionError <: Exception
y
end

"""
WithDims(q::Quantity)
WithDims(u::Units)
Returns a type of [`Unitful.Quantity`](@ref) with the dimensions contrained to the
dimension of `q` or `u`.
Useful to build unitful interfaces that don't contrain the numeric type of the specific unit.

Examples:

```jldoctest
julia> circumference_of_square(side::WithDims(u"m")) = 4*side;
julia> circumference_of_square((1//2)m) # works
2//1 m
julia> circumference_of_square((1//2)km) # also works
2//1 km
```

See also [`Unitful.WithUnits`](@ref).
"""
WithDims(q::Quantity) = Quantity{T, dimension(q), U} where {T<:Real, U<:Unitlike}
WithDims(u::Units) = Quantity{T, dimension(u), U} where {T<:Real, U<:Unitlike}

"""
WithUnits(q::Quantity)
WithUnits(u::Units)
Returns a type of [`Unitful.Quantity`](@ref) with the dimensions and units contrained to the
dimension and units of `q` or `u`.
Useful to build unitful interfaces that don't contrain the numeric type.

Examples:

```jldoctest
julia> circumference_of_square(side::WithUnits(u"m")) = 4*side;
julia> circumference_of_square((1//2)m) # works
2//1 m
julia> # circumference_of_square((1//2)km) # doesn't work, constrained to exactly meters
```

See also [`Unitful.WithDims`](@ref).
"""
WithUnits(q::Quantity) = Quantity{T, dimension(q), unit(q)} where {T<:Real}
WithUnits(u::Units) = Quantity{T, dimension(u), typeof(u)} where {T<:Real}

Base.showerror(io::IO, e::DimensionError) =
print(io, "DimensionError: $(e.x) and $(e.y) are not dimensionally compatible.");

Expand Down
13 changes: 13 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2071,6 +2071,7 @@ end
Base.:+(a::Num, b::Num) = Num(a.x + b.x)
Base.:-(a::Num, b::Num) = Num(a.x - b.x)
Base.:*(a::Num, b::Num) = Num(a.x * b.x)
Base.:<(a::Num, b::Num) = a.x < b.x
Base.promote_rule(::Type{Num}, ::Type{<:Real}) = Num
Base.ArithmeticStyle(::Type{Num}) = Base.ArithmeticRounds()
Base.OrderStyle(::Type{Num}) = Base.Unordered()
Expand All @@ -2079,6 +2080,18 @@ Base.OrderStyle(::Type{Num}) = Base.Unordered()
# Test that @generated functions work with Quantities + custom types (#231)
@test uconvert(u"°C", Num(373.15)u"K") == Num(100)u"°C"
end
area_of_circle(radius::WithDims(u"m")) = pi*radius^2
area_of_square(side::WithUnits(u"m")) = side^2

@testset "Unitful interfaces" begin
@test area_of_circle(Num(1.0)u"m") ≈ pi*m^2
@test area_of_circle(Num(1.0)u"km") ≈ pi*km^2
@test_throws MethodError area_of_circle(Num(1.0)u"s")

@test area_of_square(Num(0.5)u"m") ≈ 0.25*m^2
@test_throws MethodError area_of_square(Num(0.5)u"km")
@test_throws MethodError area_of_square(Num(0.5)u"s")
end

@testset "Traits" begin
@testset "> ArithmeticStyle" begin
Expand Down