Unitful.jl icon indicating copy to clipboard operation
Unitful.jl copied to clipboard

Unitful.Angle?

Open m-wells opened this issue 5 years ago • 11 comments

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.

m-wells avatar Mar 27 '19 04:03 m-wells

It's already a thing https://github.com/yakir12/UnitfulAngles.jl :wink:

giordano avatar Mar 27 '19 06:03 giordano

@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"

m-wells avatar Mar 27 '19 08:03 m-wells

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 like Unitful.Time.

Unless you really want to restrict to Ints, 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 avatar Mar 27 '19 10:03 giordano

@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

m-wells avatar Mar 27 '19 13:03 m-wells

@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

giordano avatar Mar 27 '19 16:03 giordano

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?

m-wells avatar Mar 27 '19 17:03 m-wells

I've opened a (fairly ambitious) pull request #221. I would appreciate feedback.

m-wells avatar Apr 09 '19 14:04 m-wells

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.

ajkeller34 avatar Jun 12 '19 05:06 ajkeller34

Angles may be dimensionless, but they certainly aren't context-less. There are very specific things you can do with an angle

bhalonen avatar Jul 10 '19 15:07 bhalonen

I just created a DimensionfulAngles.jl package. It treats angles as a dimension which allows for dispatching. Check it out!

cmichelenstrofer avatar Jan 05 '23 01:01 cmichelenstrofer

jariji avatar Apr 05 '23 04:04 jariji