Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define helpers for unitful interfaces. #698

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "Unitful"
uuid = "1986cc42-f94f-5a68-af5c-568840ba703d"
version = "1.18.0"
version = "1.9.0"

[deps]
ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9"
Expand Down
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
54 changes: 54 additions & 0 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,60 @@ struct DimensionError <: Exception
y
end

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

Examples:

```jldoctest
julia> using Unitful, Unitful.DefaultSymbols; import Unitful.hr
julia> circumference_of_square(side::WithDims(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
# You can also constrain the return type. The numeric type is usually inferred automatically.
julia> kinetic_energy(mass::WithDims(kg), velocity::WithDims(m/s))::WithDims(J) = mass*velocity^2;
julia> kinetic_energy(1000kg, 100km/hr)
10000000 kg km^2 hr^-2
```

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 subtype of [`Unitful.Quantity`](@ref) with the dimensions and units constrained to the
dimension and units of `q` or `u`.
Useful to build unitful interfaces that don't constrain the unit, but not the numeric type of a quantity.

Examples:

```jldoctest
julia> using Unitful, Unitful.DefaultSymbols; import Unitful.hr
julia> circumference_of_square(side::WithUnits(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

# You can also constrain the return type. The numeric type is usually inferred automatically.
julia> kinetic_energy(mass::WithUnits(kg), velocity::WithUnits(m/s))::WithUnits(J) = mass*velocity^2 |> x->uconvert(J, x)
julia> kinetic_energy(1000kg, uconvert(m/s, 100km/hr))
62500000//81 J
```

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
18 changes: 18 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2080,6 +2080,24 @@ Base.OrderStyle(::Type{Num}) = Base.Unordered()
@test uconvert(u"°C", Num(373.15)u"K") == Num(100)u"°C"
end

@testset "WithDims, WithUnits" begin
# built-in types
@test 1m isa WithDims(m)
@test 1.0m isa WithDims(m)
@test 1//1m isa WithDims(m)
@test 1.0m isa WithUnits(m)
# user-defined types
@test Num(1.0)m isa WithDims(m)
@test Num(1.0)km isa WithDims(m)
@test !(Num(1.0)s isa WithDims(m))
@test Num(1.0)m isa WithUnits(m)
@test !(Num(1.0)km isa WithUnits(m))
@test !(Num(1.0)s isa WithUnits(m))
# composite units
@test 1kg*(1.0m/s)^2 isa WithDims(J)
@test 1kg*(1.0m/s)^2 isa WithUnits(kg*m^2/s^2)
end

@testset "Traits" begin
@testset "> ArithmeticStyle" begin
@test Base.ArithmeticStyle(1m) === Base.ArithmeticWraps()
Expand Down