Between dealing with degrees versus radians, their specialized functions (e.g. cosd
versus cos
), optimizing their representation (wrapping around, defining counter- and clockwise, etc), and using Unitful.jl
, I started thinking, should Julia
have a native Angle
type…? Will the overhead not be worth the gain?
It’s not an overhead issue - the question is “what should be in Base” vs “what should be in packages”.
On one hand Unitful seems like the perfect place for these units. On the other hand, Base defines (and therefore “owns”) trigonometry functions like sin
and sind
and therefore obviously has a pretty good idea of what radians and degrees are, and interface improvements are always welcome. It might be possible something simple and subtle would be accepted, but having all possible windings and sense of directions… this seems less likely to me.
I have a feeling that forcing everyone to predefine quantities that go into a sin
function as Degree
or Radian
(both subtypes of Angle
) wouldn’t be very popular. But it does make some sense… On the other hand, what should acos
return, Degree
or Radian
…? hmm…
Just out of curiosity, in what context would you want acos(x)
to return degrees?
Degrees are convenient because people find small, conveniently factorizable integers easy to work with — eg 60° is more intuitive to most non-mathematicians than π/3. But when you use an arbitrary argument to acos
, you will get some float anyway, and getting that in degrees instead of radians may not be that large of an advantage.
Over at the libcairo mailing list there is currently a medium sized discussion to add API to deal with angles in deg because angles in deg (esp. 30/45/90 multiples) have more ‘meaning’ and lead to exact transformation matrices. It’s really a matter of perspective and personal preferences.
(read as: you get numerical problems, as irrational numbers like pi/3 cannot be represented in float)
The issue you mention is well understood, and Julia has dealt with it already (eg Base.cospi
). The question was about acos
. I am not arguing that a version that gives degrees is not useful, merely asking for examples in actual code; I think they would focus the discussion.
The usefulness of acos
returning degrees is not different from any other situation where angles are expressed in terms of degrees instead of radians. They are easier for biologists (in my case).
Normally you’d never touch degrees and therefore never worry about which is it, but if the interface with the user includes the possibility of degrees, then this becomes a concern (which one can solve locally). It would be nice to not worry about it. Theoretically, the type of angle acos
would return could be assigned randomly and things would still just work…
A wild thought, with a Angle type, we could have specialized angles that are multiples of pi (or 180, depending on the type), and therefore would elicit the cospi automatically. cool…
Base.cospi is not really a matching example, as:
Compute cos(πx) more accurately than cos(pi*x), especially for large x.
and 1/3 still isn’t represented in float.
Indeed. Try
cospi(1//3)
It occurs to me that there are a bunch of cases where julia’s type system could be used to introduce cool functionalities when it comes to these entities that traditionally are not considered “worth” typing. Angle is the subject of this thread, but Sign is another. Examples of such entities that I think are not typed (or less so) in other languages are Time, Diagonal, Irrational, etc. It’s not entirely clear to me why these get their own (native) type and, say, Angle doesn’t.
It seems like there’s a huge potential to refactor trig functions. Here’s a list of sin family functions in base exports; there’s a similar family for every trig function.
asin,
asind,
asinh,
sin,
sinc,
sind,
sinh,
sinpi
It seems like this would better be handled with dispatch and/or keyword arguments? Just keyword arguments like hyperbolic = true
, degrees = true
, and inverse = true
would go a long way.
A combination of angle types (AngleInDegrees
, AngleInRadians
, AngleInPis
) and keywords (hyperbolic
, cardinal
, inverse
, cofunction
and reciprocal
) could in fact get you down to exactly two trig functions (sin
and tan
). Of course this is a bit extreme, but so is the amount of trig exports in base.
Branching on arguments is too slow. Trig functions are expected to run as fast as possible.
Also, the names are standard across almost every language that has these functions, so changing them would confuse a lot of users.
There shouldn’t be a problem with adding a few methods for an Angle type. So people will always be able to run sin
on a float, but they could also run sin
on a Degree <: Angle
, and sin
will behave like sind
.
And not just programming languages. These names mostly correspond to the names used in mathematics. They are different functions, and should have different names.
Agreed, but sind
versus sin
isn’t a “real” mathematical distinction (or is it?). On the other hand, that’s something an Angle type might take care of.
Yes, sin and sind is a different story.
@yakir12, there are two issues here:
- would a separate
Angle
type be useful? - should this be part of
Base
?
A priori arguments can only get you so far; the best way to demonstrate 1. is to write a package and see if it gets used. This should also help you refine the interface.
I guess 2. will only be relevant if the package is widely used, and you demonstrate that inclusion in Base
could achieve something that a package could not. That’s a tall order, but even if you think so, it comes after the package.
Unitful does the correct thing already with respect to sin
vs sind
:
julia> using Unitful.°
julia> sin(720π/180)
-4.898587196589413e-16
julia> sin(720°)
0.0
julia> sind(720)
0.0