Unitful.jl
Unitful.jl copied to clipboard
Unitful.Angle?
Perhaps a callback to #38 I think it would be useful to define an Angle "type".
I've hacked this
const Angle = Union{typeof(1rad),typeof(1.0rad),typeof(1°),typeof(1.0°)}
I don't see how this would be inherently different from something like Unitful.Time
.
This is most useful in terms of dispatch. Structures benefit from concrete types.
It's already a thing https://github.com/yakir12/UnitfulAngles.jl :wink:
@giordano Thanks for the reply but I don't see a type declaration that would allow me to do something like
f(x::UnitfulAngles.Angle) = sin(x)
where x
could be x=2u"°"
or x=2u"rad"
Ah, if you really want a type then no, there is no new type nor alias, there.
I've hacked this
const Angle = Union{typeof(1rad),typeof(1.0rad),typeof(1°),typeof(1.0°)}
I don't see how this would be inherently different from something likeUnitful.Time
.
Unless you really want to restrict to Int
s, I'd suggest you to define the constant as
const Angle{T} = Quantity{T, NoDims, u"rad"}
which is independent from the type of the value
@giordano The method you suggest yields the following
julia> using Unitful
julia> const Angle{T} = Quantity{T,NoDims,u"rad"}
Quantity{T,NoDims,rad} where T
julia> f(x::Angle) = sin(x)
f (generic function with 1 method)
julia> f(1.2u"°")
ERROR: MethodError: no method matching f(::Quantity{Float64,NoDims,Unitful.FreeUnits{(°,),NoDims,nothing}})
Closest candidates are:
f(::Quantity{T,NoDims,rad} where T) at REPL[3]:1
Stacktrace:
[1] top-level scope at none:0
julia> f(1.2u"rad")
ERROR: MethodError: no method matching f(::Quantity{Float64,NoDims,Unitful.FreeUnits{(rad,),NoDims,nothing}})
Closest candidates are:
f(::Quantity{T,NoDims,rad} where T) at REPL[3]:1
Stacktrace:
[1] top-level scope at none:0
while my hack yields what I find to be the desired behavior
julia> import Unitful: °, rad
julia> const Angle = Union{typeof(1rad),typeof(1.0rad),typeof(1°),typeof(1.0°)}
Union{Quantity{Float64,NoDims,FreeUnits{(rad,),NoDims,nothing}}, Quantity{Float64,NoDims,FreeUnits{(°,),NoDims,nothing}}, Quantity{Int64,NoDims,FreeUnits{(rad,),NoDims,nothing}}, Quantity{Int64,NoDims,FreeUnits{(°,),NoDims,nothing}}}
julia> f(x::Angle) = sin(x)
f (generic function with 1 method)
julia> f(30°)
0.5
julia> f(π*rad/6)
0.49999999999999994
julia> f(30)
ERROR: MethodError: no method matching f(::Int64)
Closest candidates are:
f(::Union{Quantity{Float64,NoDims,FreeUnits{(rad,),NoDims,nothing}}, Quantity{Float64,NoDims,FreeUnits{(°,),NoDims,nothing}}, Quantity{Int64,NoDims,FreeUnits{(rad,),NoDims,nothing}}, Quantity{Int64,NoDims,FreeUnits{(°,),NoDims,nothing}}}) at REPL[3]:1
Stacktrace:
[1] top-level scope at none:0
It also isn't restricted to Int
julia> f(30.1°)
0.5015107371594574
@giordano The method you suggest yields the following
That's because I misspelled the last argument to Quantity
, it should have been
julia> const Angle{T} = Quantity{T, NoDims, typeof(u"rad")}
Quantity{T,NoDims,Unitful.FreeUnits{(rad,),NoDims,nothing}} where T
julia> f(x::Angle{T}) where {T<:Number} = sin(x)
f (generic function with 1 method)
julia> f(pi*u"rad")
1.2246467991473532e-16
julia> f(1u"rad")
0.8414709848078965
julia> f(3.14u"rad")
0.0015926529164868282
Your solution works only with the type you pass to the Union
, not with any Number
type, including custom ones. For example, with Irrational
(which is not even custom but a standard one) you get:
julia> f(pi*u"rad")
ERROR: MethodError: no method matching f(::Quantity{Irrational{:π},NoDims,Unitful.FreeUnits{(rad,),NoDims,nothing}})
Closest candidates are:
f(::Union{Quantity{Float64,NoDims,FreeUnits{(rad,),NoDims,nothing}}, Quantity{Float64,NoDims,FreeUnits{(°,),NoDims,nothing}}, Quantity{Int64,NoDims,FreeUnits{(rad,),NoDims,nothing}}, Quantity{Int64,NoDims,FreeUnits{(°,),NoDims,nothing}}}) at REPL[6]:1
f(::Quantity{T,NoDims,rad}) where T at REPL[10]:1
Stacktrace:
[1] top-level scope at none:0
I like your type definition of
julia> const Angle{T} = Quantity{T, NoDims, typeof(u"rad")}
Quantity{T,NoDims,Unitful.FreeUnits{(rad,),NoDims,nothing}} where T
Would this be something worth considering for inclusion in Unitful?
I've opened a (fairly ambitious) pull request #221. I would appreciate feedback.
I don't think there is a perfect solution for dispatching on a Unitful.Angle
type, because a) angles are dimensionless (see https://github.com/PainterQubits/Unitful.jl/issues/38#issuecomment-254578080) and b) one could always define new angle units, which means any union of types you define to try and cover all angle types won't be generic. My impression is that most people use either radians or degrees, in which case you could define the following, or make an even larger union with other units:
Angle{T} = Union{Quantity{T,NoDims,typeof(u"rad")}, Quantity{T,NoDims,typeof(u"°")}} where T
@m-wells this is of course a simple extension of what @giordano wrote but I wanted to be sure you realized you had this option.
Angles may be dimensionless, but they certainly aren't context-less. There are very specific things you can do with an angle
I just created a DimensionfulAngles.jl package. It treats angles as a dimension which allows for dispatching. Check it out!
-
"On the dimension of angles and their units" (2022) makes the case for an angle dimension.
-
"Angles are inherently neither length ratios nor dimensionless" (2019) addresses the circumference issue discussed in https://github.com/PainterQubits/Unitful.jl/issues/38#issuecomment-254578080.