Skip to content

Commit 55a5d13

Browse files
committed
Define helpers for unitful interfaces.
This commit adds two simple functions `similar_dims` (and `similar_units`) that return a `Quantity` type with the dimensions (and units) constrained to those of the parameters I found myself trying to write simulation code with strongly typed interfaces, i.e. including information about the units. Initially I wrote my interfaces like so: ```julia const Meters = typeof(1.0m); circumference_of_circle(r::Meters) = pi*r^2 ``` However, when trying to autodiff through this code, I run into a problem, because `Meters` has the numerical type `Float64` baked in, and autodiff evaluates on a type `Quantity{Dual{Float64}}` (roughly). We can instead define `Meters` like so: ```julia const Meters{T<:Real} = Quantity{T, dimension(1.0m), unit(1.0m)} circumference_of_circle(r::Meters{T}) where {T} = pi*r^2 circumference_of_circle(r::Quantity{T, dimension(1.0m), unit(1.0m)}) where {T} = pi*r^2 ``` but I thought a better approach would be to provide some syntactic sugar to this "unit constraint". With this PR, we can write ```julia circumference_of_circle(r::similar_dims(u"m")) where {T} = pi*r^2 circumference_of_circle(r::similar_units(u"m")) where {T} = pi*r^2 ``` The difference is that the first one only constrains the dimension, and the latter constrains both dimension and unit (i.e. doesn't allow e.g. `km`). I'm happy to receive any feedback on the idea and the naming. Other names could be e.g. `quantity_with_dims` (but too long for my taste), or `dims_as` etc., but `similar` is already Julia lingo and feels appropriate in this context.
1 parent a24bc69 commit 55a5d13

File tree

2 files changed

+44
-0
lines changed

2 files changed

+44
-0
lines changed

src/Unitful.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import LinearAlgebra: istril, istriu, norm
2727
import Random
2828

2929
export logunit, unit, absoluteunit, dimension, uconvert, ustrip, upreferred
30+
export similar_units, similar_dims
3031
export @dimension, @derived_dimension, @refunit, @unit, @affineunit, @u_str
3132
export Quantity, DimensionlessQuantity, NoUnits, NoDims
3233

src/utils.jl

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,49 @@ struct DimensionError <: Exception
243243
y
244244
end
245245

246+
"""
247+
similar_dims(q::Quantity)
248+
similar_dims(u::Units)
249+
Returns a type of [`Unitful.Quantity`](@ref) with the dimensions contrained to the
250+
dimension of `q` or `u`.
251+
Useful to build unitful interfaces that don't contrain the numeric type of the specific unit.
252+
253+
Examples:
254+
255+
```jldoctest
256+
julia> circumference_of_square(side::similar_dims(u"m")) = 4*side;
257+
julia> circumference_of_square((1//2)m) # works
258+
2//1 m
259+
julia> circumference_of_square((1//2)km) # also works
260+
2//1 km
261+
```
262+
263+
See also [`Unitful.similar_units`](@ref).
264+
"""
265+
similar_dims(q::Quantity) = Quantity{T, dimension(q), U} where {T<:Real, U<:Unitlike}
266+
similar_dims(u::Units) = Quantity{T, dimension(u), U} where {T<:Real, U<:Unitlike}
267+
268+
"""
269+
similar_units(q::Quantity)
270+
similar_units(u::Units)
271+
Returns a type of [`Unitful.Quantity`](@ref) with the dimensions and units contrained to the
272+
dimension and units of `q` or `u`.
273+
Useful to build unitful interfaces that don't contrain the numeric type.
274+
275+
Examples:
276+
277+
```jldoctest
278+
julia> circumference_of_square(side::similar_units(u"m")) = 4*side;
279+
julia> circumference_of_square((1//2)m) # works
280+
2//1 m
281+
julia> # circumference_of_square((1//2)km) # doesn't work, constrained to exactly meters
282+
```
283+
284+
See also [`Unitful.similar_dims`](@ref).
285+
"""
286+
similar_units(q::Quantity) = Quantity{T, dimension(q), unit(q)} where {T<:Real}
287+
similar_units(u::Units) = Quantity{T, dimension(u), typeof(u)} where {T<:Real}
288+
246289
Base.showerror(io::IO, e::DimensionError) =
247290
print(io, "DimensionError: $(e.x) and $(e.y) are not dimensionally compatible.");
248291

0 commit comments

Comments
 (0)