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

Introducing Angle type [Issue #88] #169

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open

Conversation

jkalias
Copy link

@jkalias jkalias commented Dec 8, 2020

@stephentyrone Here is my implementation for the issue. Please let me know if I missed something.

@stephentyrone
Copy link
Member

There's a few things that I would want to resolve before merging this (this is based on a quick skim, rather than a detailed review):

  • It doesn't implement the semantics that one would want of an Angle type; for example, Angle(degrees: 90).cos should be exactly zero, but it is not with this implementation.

  • It's a rather large departure from the existing APIs for ElementaryFunctions and normal mathematical notation in making them properties instead of static functions (.cos(angle)) or free functions (cos(angle)).

  • It's not obvious that the hyperbolic functions really makes sense for an angle type (there's no "angle" in the usual geometric interpretation of these functions to take in degrees or radians, rather an area).

@jkalias
Copy link
Author

jkalias commented Dec 9, 2020

I have addressed your comments, I hope this is along the lines you envisioned.

There's a few things that I would want to resolve before merging this (this is based on a quick skim, rather than a detailed review):

  • It doesn't implement the semantics that one would want of an Angle type; for example, Angle(degrees: 90).cos should be exactly zero, but it is not with this implementation.

You are right, thanks for bringing this up. I added more tests covering other special cases, eg. 0, 45, 180, 270 etc

  • It's a rather large departure from the existing APIs for ElementaryFunctions and normal mathematical notation in making them properties instead of static functions (.cos(angle)) or free functions (cos(angle)).

They are now static functions.

  • It's not obvious that the hyperbolic functions really makes sense for an angle type (there's no "angle" in the usual geometric interpretation of these functions to take in degrees or radians, rather an area).

🙈, my bad. You are absolutely right. I removed the hyperbolic functions.

}

static func specialDegreesTrigonometricFunctionChecks() {
assertClose(1, cos(Angle<Self>(degrees: -360)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests with exact results should test exact equality, not assertClose.

@stephentyrone
Copy link
Member

stephentyrone commented Dec 9, 2020

This approach doesn't work to make exact cases of degrees exact (or rather it does, but unfortunately introduces enormous errors for radian arguments; this would make cos(Angle(.pi/2)), which should be non-zero, return zero. This is solvable, but not by massaging the trigonometric functions themselves; it has to be handled in the angle initializer instead. I'll put together a sketch showing how to do it correctly.

@jkalias
Copy link
Author

jkalias commented Dec 9, 2020

Ok, thanks for the effort

The tangent tests are not passing, this should be fixed.
@jkalias
Copy link
Author

jkalias commented Dec 9, 2020

This approach doesn't work to make exact cases of degrees exact (or rather it does, but unfortunately introduces enormous errors for radian arguments; this would make cos(Angle(.pi/2)), which should be non-zero, return zero. This is solvable, but not by massaging the trigonometric functions themselves; it has to be handled in the angle initializer instead. I'll put together a sketch showing how to do it correctly.

Why should cos(Angle(.pi/2)) not be zero by the way??

@stephentyrone
Copy link
Member

stephentyrone commented Dec 9, 2020

Because .pi is not exactly π (π is not exactly representable in any FloatingPoint type).

@jkalias
Copy link
Author

jkalias commented Dec 9, 2020

What would you think of another approach: keeping track of which initializer was used like this

public struct Angle<T: Real & BinaryFloatingPoint> {
    public var radians: T {
        switch input {
        case let .radians(radiansValue):
            return radiansValue
        case let .degrees(degreeValue):
            return degreeValue * Angle.radiansPerDegree
        }
    }
    
    public init(radians: T) { self.input = .radians(radians) }
    public static func radians(_ val: T) -> Angle<T> { .init(radians: val) }
    
    public var degrees: T {
        switch input {
        case let .radians(radiansValue):
            return radiansValue * Angle.degreesPerRadian
        case let .degrees(degreeValue):
            return degreeValue
        }
    }
    public init(degrees: T) { self.input = .degrees(degrees) }
    public static func degrees(_ val: T) -> Angle<T> { .init(degrees: val) }
    
    private enum Input {
        case degrees(T)
        case radians(T)
    }
    
    private let input: Input
    
    private static var degreesPerRadian: T { 180 / .pi }
    
    private static var radiansPerDegree: T { .pi / 180 }
}

Then in the trig functions, we could switch on the .input: if radians, then use directly the corresponding trig function. For degrees, we could try to find if the input is an integer multiple of 30deg and/or 45deg and decide accordingly.

@NevinBR
Copy link
Contributor

NevinBR commented Dec 13, 2020

A few thoughts:

Why is there a constraint to BinaryFloatingPoint? Is Real not sufficient?

Why is normalize a private free function, rather than a public instance method normalized()?

Some applications need to represent angles greater than a full rotation.

One of the common things I want to check is “Does angle α fall between angles β and γ?” Or similarly, “Is angle δ no more than angle ε away from angle ζ?”

Shouldn’t Angle conform to AdditiveArithmetic? And allow scalar multiplication?

@jkalias
Copy link
Author

jkalias commented Dec 13, 2020

A few thoughts:

Why is there a constraint to BinaryFloatingPoint? Is Real not sufficient?

True, it's removed now. Can't remember why I put it there in the first place.

Why is normalize a private free function, rather than a public instance method normalized()?
Some applications need to represent angles greater than a full rotation.

This was done in the effort to massage the trigonometric functions, as @stephentyrone pointed out in an earlier comment, so that angles initialized with special values for degrees (eg. 90) give exact results. @stephentyrone mentioned though that this approach is not right, and he will prepare a draft of what he thinks is the appropriate solution path, so I am waiting for this.

One of the common things I want to check is “Does angle α fall between angles β and γ?” Or similarly, “Is angle δ no more than angle ε away from angle ζ?”

Ok, makes sense, will implement.

Shouldn’t Angle conform to AdditiveArithmetic? And allow scalar multiplication?

Correct, it's done.

@@ -160,6 +160,12 @@ public extension Angle {
}
}

extension Angle: Equatable {
public static func == (lhs: Angle<T>, rhs: Angle<T>) -> Bool {
lhs.radians.isApproximatelyEqual(to: rhs.radians)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementations of == cannot use approximate equality because it does not fulfill the semantic requirements of ==.

public static var zero: Angle<T> { .radians(0) }

public static func + (lhs: Angle<T>, rhs: Angle<T>) -> Angle<T> {
Angle(radians: lhs.radians + rhs.radians)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems unfortunate to me that adding two angles specified in degrees incurs rounding error seven times if I want the result in degrees. I think this dedicated type needs to store values given in degrees as-is if it offers such arithmetic.

Copy link
Author

@jkalias jkalias Dec 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what I intended with my earlier comment, so degrees and radians are stored separately.
#169 (comment)

We could then switch on all operations performed on the input and essentially handle three cases:

  • degrees with degrees -> perform operation in degrees
  • radians with radians -> perform operation in radians
  • mixed units -> perform in a common unit

I'm not sure whether people agree on that

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 That would yield a superior result in the case of operations performed entirely in degrees or radians.

I wonder if there are alternative designs that can improve the result when working with both degrees and radians. If the type stored both radians and degrees, for example, then you could store the result of "90 degrees plus 1 radian" exactly. I haven't thought through the remainder of the design, but I offer it for your consideration here.

/// - Parameters:
///
/// - range: The closed angular range within which containment is checked.
func contained(in range: ClosedRange<Angle<T>>) -> Bool {
Copy link
Contributor

@xwu xwu Dec 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the value of these APIs over what's written in the body? I would suggest not including these separately; the existing forms are canonical and straightforward enough to write.

What @NevinBR is getting at, I think, is that 361 degrees is contained "between" 0 degrees and 2 degrees. This does not offer that functionality, and convenient access to it would be useful. It would need to be distinguished from the functionality here, though, through some thoughtful naming.

For API naming purposes, incidentally, the function would need to be named isContained; and for style purposes, the access modifier public should be specified on each member (and therefore is not necessary on the extension itself).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What @NevinBR is getting at, I think, is that 361 degrees is contained "between" 0 degrees and 2 degrees. This does not offer that functionality.

Additionally, 175º and -175º are both “between” 170º and -170º (but they are not between -170º and 170º).

Copy link
Author

@jkalias jkalias Dec 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, essentially you mean @NevinBR that the containment should be performed on the normalized angular range, right?

361º -> normalized to 1º -> contained in -10º...10º
361º -> normalized to 1º -> not contained in 10º...-10º
175º -> normalized to 175º -> contained in -170º...170º
-175º -> normalized to -175º -> contained in -170º...170º
175º -> normalized to 175º -> not contained in 170º...-170º
-175º -> normalized to -175º -> not contained in 170º...-170º

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not entirely sure how normalization should come into play. Probably yes normalized angles should be used for comparison / containment, and if someone wants to work with the unnormalized value they can extract it and do so manually.

However, several of your examples do not behave as I would expect:

175º -> normalized to 175º -> contained in -170º...170º
-175º -> normalized to -175º -> contained in -170º...170º
175º -> normalized to 175º -> not contained in 170º...-170º
-175º -> normalized to -175º -> not contained in 170º...-170º

These should be:

175º -> normalized to 175º -> not contained in -170º...170º
-175º -> normalized to -175º -> not contained in -170º...170º
175º -> normalized to 175º -> yes contained in 170º...-170º
-175º -> normalized to -175º -> yes contained in 170º...-170º

@jkalias
Copy link
Author

jkalias commented Jan 11, 2021

Hello and happy/healthy new year to everybody.

It's not clear to me if there is something I need to do here, or if people are still thinking about it. Any feedback would be deeply appreciated.

@danieljfarrell
Copy link

I don’t like this at all.

It introduces additional complexity for very little benefit. I much prefer keeping the types simple as possible and making the functions well documented or with function signatures which make this obvious.

Generally everything should be in radians in numerical code. I’m all for providing convenience functions which convert between angular definitions; such as in numpy https://numpy.org/doc/stable/reference/generated/numpy.degrees.html

@jkalias
Copy link
Author

jkalias commented Jan 20, 2021

I am all in for radians in numerical code. However, judging from previous comments, it seems that the community does not accept inaccuracies for degrees inputs (eg. cos(Angle(degrees: 90)) != 0).

I'd personally lean more towards a wrapper of Measurement<UnitAngle> which already handles transformations between various inputs, and then explicitly using the radians value in trigonometric functions.

@danieljfarrell
Copy link

Yes, that’s the kind of thing that people just starting out with numerical code might find surprising. Anyone with some some experience or understanding of floats will accept this, and is aware of it.

Who is the intended audience of the library? If it’s to encourage swift as a machine learning, data, scientific programming language, then sticking to conventional numerical programming norms is much more likely to get this adopted and used.

@superlopuh
Copy link

This approach doesn't work to make exact cases of degrees exact (or rather it does, but unfortunately introduces enormous errors for radian arguments; this would make cos(Angle(.pi/2)), which should be non-zero, return zero. This is solvable, but not by massaging the trigonometric functions themselves; it has to be handled in the angle initializer instead. I'll put together a sketch showing how to do it correctly.

Wouldn't it make sense to only promise exact results for values created entirely from Angles? If the values are represented as turns, then cos(Angle.pi / 2) should be exact, since Angle.pi would be represented as 1.0 or 0.5 internally, depending on whether the backing range is 0..<1 or -0.5..<0.5 or -1..<1. (I would prefer the spelling Angle(turns: 0.5)) Even if the internal representation is different, it seems like promising exact results for values that have been converted from floating-point representations will be difficult in the general case.

@jkalias
Copy link
Author

jkalias commented Jan 23, 2021

I think we need to first answer what @danieljfarrell mentioned: what is the primary use case of swift-numerics and what is the target audience.

According to the introduction section, "Swift Numerics provides a set of modules that support numerical computing in Swift". My experience with numerical computing is using exclusively radians, which is reflected in my first PR.

However it seems @stephentyrone has some objections regarding inaccuracies for special degrees cases. I have also never heard of turns in numerical code, but I'm definitely not an authority on the issue (and it's not my intention to sound like one)

@stephentyrone
Copy link
Member

My goal for Swift Numerics is to guide people to solutions that eliminate as many sources of error as possible, and to reduce errors that cannot be eliminated. Conversion from degrees (or turns) to radians for use with trig functions is one such common source of error. From that perspective, it makes good sense to provide functions and/or types that make it easier to avoid error--but only if they do not introduce new errors instead. It's the second half of this that makes "simple" solutions to this problem a little bit tricky.

Tricky, but not impossible; there are a few different ways in which this can be achieved, with slightly different tradeoffs. My sketchy design for a solution right now is along the following lines:

  • internally angles can be represented in fractions of π from -1 ... 1.
  • initialization from degrees is pretty easy ("just" use formRemainder).
  • initialization from radians requires an "infinite π" reduction. This is not hard for any concrete type (see Payne & Hanek's original paper, or K.C. Ng's more approachable "ARGUMENT REDUCTION FOR HUGE ARGUMENTS: Good to the Last Bit"). It is quite a bit more subtle to implement for arbitrary floating-point types, so a generic implementation is hard (and requires a lot of support machinery that doesn't exist in Swift or Swift Numerics yet). So this would probably have to be a protocol requirement on Real (which is fine, we can do that). Exact signature to be worked out.
  • the above together with "trig-pi" functions gives a complete solution to the simplest form of the problem ("sin(180˚) should be zero").

There are some things that the above doesn't totally solve:

  • Angles are equivalence classes so integer numbers of turns are lost. I think that's appropriate, but it's not what some people will want.
  • There's much more relative accuracy close to zero than close to +/-1. This means that angles close to (4k+{1,2,3})π/2 get less accuracy than angles close 2kπ for integer k. This can be "solved" by representing the angle as a quadrant and offset, but that is not a complete solution, because if θ is unusually close to 2kπ + π/4, then 2Angle(θ) would be computed with much less accuracy than Angle(2θ). This can't really be solved without arbitrary-precision, which is a thing we might do someday, but an Angle type shouldn't be dependent on it, so I'm provisionally calling it out-of-scope.

All of which is to say that I have a fairly complete solution worked out in my head, but I've been on new parent leave for the last little while and therefore mostly not working on such things =)

@jkalias
Copy link
Author

jkalias commented Jan 29, 2021

Fair enough.

I currently see the following options moving forward:

  1. I pause this PR and step down till @stephentyrone (or someone else ?) has more time to implement his approach
  2. I merge Trig-pi functions #85 in my branch so I have access to "trig-pi" functions and then implement a draft of the "conversion from degrees" part. The "conversion from radians" will be implemented in a second step.
  3. Other ideas from the community?

Kindly let me know what you think.

(@stephentyrone enjoy the family time :) )

@danieljfarrell
Copy link

I'm not disputing these are useful features, just that, I don't think they belong in a numerics library.

I would expect a symbolic mathematics package to correctly evaluate these as you have outlined. But numerics is not symbolics.

If we look at ecosystems that have been successful, let's take Python for an example.

numpy is fast and efficient because it's all build around a simple data structure NDArray. The core does not try to do too much other than provide useful vectorised array operations and numeric types (there are useful subpackages for handling various numerical tasks)

As numpy became ubiquitous in the Python community (it's used by over 500,000 open source projects) various packages have emerged which provide useful domain specific behaviour. This rich ecosystem is great for developers, but it is enabled by a rock solid numerical library.

My concern here is that numerical library should be fairly simple and not abstract away, or try and make floating point operations nice and tidy; because they are not. It feels almost like an anti-pattern.

I think the functionality described here is better as a third party project which has a dependency on swift-numerics, rather than being incorporated into it.

@stephentyrone
Copy link
Member

My concern here is that numerical library should be fairly simple and not abstract away, or try and make floating point operations nice and tidy; because they are not. It feels almost like an anti-pattern.

I agree, but that's the exact opposite of what's being discussed here. Rather, my goal is to pare down complex numerical algorithms to the core components that must be implemented by experts; the things that there should be a single high-quality implementation of so that all the other projects do not need to implement them themselves. An angle type isn't that, but the building blocks that make it possible (infinite-precision π/2 reduction, trig-pi functions) are.

@jkalias
Copy link
Author

jkalias commented Jan 31, 2021

I’m not sure I fully understand what is meant here.

a. An Angle type should not be defined in swift-numerics because it’s not in the intended scope of the library.
b. The Angle type should be included in swift-numerics based on the building blocks of infinite-precision π/2 reduction and trig-pi functions.

Can @stephentyrone shed some light please?

@stephentyrone
Copy link
Member

stephentyrone commented Feb 1, 2021

The building blocks for an angle type absolutely should be defined in Swift Numerics. The angle type itself might or might not be, but it almost doesn't matter either way because:

  • once you have the right building blocks, providing the type is "trivial", so another project can easily do it.
  • once you have the right building blocks, providing the type is "trivial", so there's no downside to including it in Swift Numerics.

I realize that this is not an especially clear answer. 🤷🏻‍♂️

Nonetheless, I think having your PR is great, because:

  • it lets us discuss this, which obviously needs to happen.
  • it provides an opportunity to iterate on and flesh out what an Angle API might look like, which is useful even if it ends up living somewhere else.
  • it provides a good place to discuss implementation details so we can figure out more precisely what building blocks are needed.

@jkalias
Copy link
Author

jkalias commented Mar 21, 2021

Following the previous discussions, I made an attempt this time using the trig-pi functions introduced in branch #85. I still don't have a working method for a correct argument reduction (especially for large arguments). I kept the private backing fields of both degree and radian parts, in my attempt to increase the accuracy in addition/subtraction.

@jkalias
Copy link
Author

jkalias commented May 13, 2021

Hello,

Any update on this?

@stephentyrone
Copy link
Member

Any update on this?

I don't think anything has changed since February. What are you hoping for?

@jkalias
Copy link
Author

jkalias commented May 24, 2021

I was hoping for some feedback on whether merging the trig-pi functions branch was something we want to pursue or not.

@jkalias
Copy link
Author

jkalias commented Mar 19, 2022

HI, why are the builds hanging? Do I need to do something?

@jkalias jkalias closed this Mar 23, 2022
@jkalias jkalias reopened this Mar 23, 2022
@jkalias
Copy link
Author

jkalias commented Mar 23, 2022

@swift-ci test

@jkalias
Copy link
Author

jkalias commented Mar 23, 2022

Any feedback? @karwa / @davedelong (since you were involved in the discussion of Issue #88), or @danieljfarrell

@jkalias jkalias changed the title Introduced Angle type [Issue #88] Introducing Angle type [Issue #88] May 23, 2022
@jkalias
Copy link
Author

jkalias commented May 23, 2022

Anyone from the core team who would like to bother and give some feedback?

@jkalias
Copy link
Author

jkalias commented Jul 3, 2022

I have tried several times to get some feedback about this PR, and what I can do to improve things. Unfortunately, I have the impression I am talking to a wall, and I see no interest. Perhaps this PR is now completely irrelevant with the introduction of the Spatial framework (?).

In any case this is not the kind of feedback/experience I was hoping for or the one which is being advertised by Apple ("We value our community efforts" and such).

@stephentyrone
Copy link
Member

Hi @jkalias --

I (and others) have laid out some concerns about representation that would prevent taking the PR as is upthread, but I'll summarize them here for convenience:

  • Storing degrees and radians separately doesn't make much sense to me; it makes arithmetic more complicated to implement correctly for minimal benefit.
  • Storing either degrees or radians is flawed because common values in either one do not have exact representations in the other.
  • Storing "turns" or "half turns" (radians with an implicit scale of π) is probably the correct choice for a general-purpose angle type, but this requires some additional supporting work (e.g. the trig-pi functions, which you've pulled in here, and accurate stand-alone angle reductions, which don't currently exist anywhere).

@NevinBR also offered a suggestion:
I’m not convinced storing both degrees and radians separately is worthwhile. I would lean more toward a design that stores a value (of type T) and a unit (probably a resilient enum). If we’re certain we’d only ever want to model degrees and radians then it could be a simple “isInDegrees” flag.

More broadly there's a question of where this type belongs: is it appropriate for Swift Numerics at all, and if so, what module does it belong in? It is somewhat more niche than the operations already defined in RealModule, or at least relevant to a different audience, but neither is it obvious that they don't belong there. More broadly still, there's a question of how "unit-like things" should be handled in Swift, and how angles fit into that, which also fits into the need for use cases. It's hard to answer these questions without seeing how the type is actually used--for this purpose, it would be nice to be able to point to this or another prototype on a branch or clone that people are actually using. Do you have examples that you can share that use your branch or another implementation? Does someone else? It would be really nice to be able to say something like "I've been using this prototype for a few months to do X, and it's great, but it would be even better if ..."

@jkalias
Copy link
Author

jkalias commented Jul 7, 2022

Hi @stephentyrone, thank you very much for the elaborate reply.

  • Storing degrees and radians separately doesn't make much sense to me; it makes arithmetic more complicated to implement correctly for minimal benefit.

I agree that this does not make much sense, my initial implementation was based only on radians.

  • Storing either degrees or radians is flawed because common values in either one do not have exact representations in the other.
  • Storing "turns" or "half turns" (radians with an implicit scale of π) is probably the correct choice for a general-purpose angle type, but this requires some additional supporting work (e.g. the trig-pi functions, which you've pulled in here, and accurate stand-alone angle reductions, which don't currently exist anywhere).

My understanding is that it would be beneficial to store the "turns" or "half-turns" in either degrees or radians (using a flag/enum to signify which case it is), but then we will "suffer" the accuracy loss when converting between the types. If we accept the trig-pi function merge, we can at least make sure that we have better results when evaluating the trig functions. Regarding the argument reduction part, I have no idea how something like this can be tackled, perhaps someone else better suited can tackle this in a next iteration step.

@NevinBR also offered a suggestion: I’m not convinced storing both degrees and radians separately is worthwhile. I would lean more toward a design that stores a value (of type T) and a unit (probably a resilient enum). If we’re certain we’d only ever want to model degrees and radians then it could be a simple “isInDegrees” flag.

Agree, as explained above.

More broadly there's a question of where this type belongs: is it appropriate for Swift Numerics at all, and if so, what module does it belong in? It is somewhat more niche than the operations already defined in RealModule, or at least relevant to a different audience, but neither is it obvious that they don't belong there. More broadly still, there's a question of how "unit-like things" should be handled in Swift, and how angles fit into that, which also fits into the need for use cases. It's hard to answer these questions without seeing how the type is actually used--for this purpose, it would be nice to be able to point to this or another prototype on a branch or clone that people are actually using. Do you have examples that you can share that use your branch or another implementation? Does someone else? It would be really nice to be able to say something like "I've been using this prototype for a few months to do X, and it's great, but it would be even better if ..."

I don't have a primary use case to highlight, other than the implementation of Angle I committed for Euclid. My initial goal with this PR was to provide a "correct" and "safe" way to do angle math; in my professional experience I have been scorched several times by APIs which expect an angle in double. I would like to use the compile to enforce correctness so it's impossible to do "wrong" angle operations; thereby adhering to Swift's primary goal of being a safe language :).

Can we agree on the following?

  1. Use half-turns as the backing store for Angle, with a corresponding flag to indicate degrees or radians
  2. Accept some loss when converting between degrees and radians (if stored by the "other" type) by multiplying by "pi/180" or "180/pi" accordingly
  3. Use the trig-pi functions with trig operations
  4. Leave the argument reduction for a next PR

If not, I think I should just delete this PR since I don't want to waste everybody's time for nothing.

What do you think?

@xwu
Copy link
Contributor

xwu commented Jul 8, 2022

I believe what @stephentyrone has been saying is that it is not possible to evaluate whether an Angle type should or shouldn't be included as part of this library without first having an implementation of all the building blocks, which includes argument reduction.

Once users have all the building blocks available to them, then it will be possible to consider whether additionally having this type will provide meaningfully more benefit or whether users can get most or all of those benefits by writing simple code that directly calls the building blocks themselves.

For this reason, having this PR here as a record of all of these discussions is helpful and it doesn't need to be closed.

@LePips LePips mentioned this pull request Dec 13, 2023
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants