-
-
Notifications
You must be signed in to change notification settings - Fork 48
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
Introduce new numerics #200
base: main
Are you sure you want to change the base?
Conversation
Is there a reason to tie these different packages together? Would there be something we lose by discussing each individually? |
Co-authored-by: Joe Eli McIlvain <[email protected]>
I expect that you mean to suggest creation of discrete RFCs. As I answered for the "primary goals" comment for clarification, this RFC is meant to "expand" math is two ways. The first is to expand by providing structure, the second by providing functionality. If we want to make this RFC only the former, then I open RFCs for each subpackage that is fine by me. I thought opening with the initial additions as discussed (a long time ago) in Zulip was "better" than breaking it up without any discussion as to how we want to move towards a more expanded math package. |
@rhagenson if these aren't tried to one another, I think that an RFC for each would be good. I would focus on adding support for things like Complex numbers, Rational numbers, and those because the design issues for each of those is worthy of individual RFCs. I'm not even sure that Rational numbers should go in And fairly reasonable argument could be made for Arbitrary precision numbers to go into builtin. I think we would decide not to, but the argument could reasonably be made based on consistency of F32 and friends being there. |
I am not against breaking this RFC into pieces, but wanted to present all the initial pieces at once. I am personally in favor of the addition functionality discussed here being outside of builtin since it will rely on builtin, but builtin will not rely on any of the suggested additions. |
During Sync on 2022-03-01 I wrote down the following notes for how to improve this RFC:
|
@SeanTAllen Would me switching this to a draft PR prevent the discuss during sync label from being reapplied? I have doubts that I will get all the updates done before next Sync in time for folks to review the changes. I have no issue with more reviews/comments/discussion, but want to prevent issues of this RFC continually reappearing for Sync with only small or in-progress edits. My current plan is:
|
No. See https://github.com/ponylang/rfcs/blob/main/.github/workflows/add-discuss-during-sync.yml#L3 for events that cause the label to be added. |
|
On Sync 2022-03-08, we discussed breaking this RFC into smaller dedicated RFCs with this RFC/PR specifically becoming a "Introduce new numerics; Rational, BigInt, BigFloat, Complex" and splitting the other content off to better allow us to discuss the merits of each addition/change. |
I'm in favor of breaking this apart into different RFCs. |
Apologies about temporarily closing this -- I tried changing the name of the |
|
||
1. restructure the `math` package into distinct subpackages; allowing for separation of concerns over continuing to build a monolithic `math` package | ||
2. provide common `math` data types; for example, `BigInt`, `Rational`, and `Complex` | ||
I propose we add the aforementioned numeric types into `builtin` so they exist alongside the other standard numeric types. These introduced numeric types **must** existing within the current numeric type hierarchy by being compliant with existing numeric traits. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For me, the preference would be to keep builtin
as minimal as possible, so I'd prefer to put these new types in a new package (or series of packages), unless there is a strong motivation for it to go in builtin
.
Pretty much all of the existing public types in builtin
have hard requirements forcing them to be there, with a reason like one of the following:
- they are used by core language constructs (
None
, string literals, numeric literals, etc) - they need to use raw pointers (
Array
,String
, etc) - they use
compile_intrinsic
for one or more function definitions - they are part of the
Env
, which is passed to theMain
actor on entry - they are used by one of the types that meet the above criteria
I don't think these new types have any hard requirements forcing them to go in builtin
, so I believe they shouldn't be there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can discuss this further on Sync and I will update the RFC according to our conversation. I have no particular need for these to be added to builtin
so no objection to an agreed upon other location such as a newly created numerics
or adding these to math
(as was the RFC state prior to my latest changes).
Notes from Sync 2022-03-15:
|
new val min_value() | ||
new val max_value() | ||
... | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand that this has already been present, but given that this is re-design I'm not clear on the benefit of these constructors, but there's some obvious costs. Is there a use case for max_value() / min_value() that makes sense for all numbers? Given that this doesn't work for BigInt/BigFloat/similar types I'd be hesitant to place it at the top of the hierarchy.
Can we instead make a Bounded interface? This will only break code which relies on these methods being present in any Real[A]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand what you are suggesting. What's a "bounded interface"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think he means a new interface whose name is Bounded
, which has the min and max value methods on it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jasoncarr0 is that what you meant? re: Bounded
interface? if yes, did you specifically mean structural typing or were you using "interface" more loosely?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant a literal Pony interface, that is:
These constructors would be part of an interface named Bounded
, while the other methods would be part of this trait.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually there is also an argument for removing Ordered, but only because this trait is really the lowest possible. Mostly that the implementations for things like matrices would be sketch but making them numerics makes sense and is powerful.
fun op_and(y: A): A => this and y | ||
fun op_or(y: A): A => this or y | ||
fun op_xor(y: A): A => this xor y | ||
fun op_not(): A => not this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can't be implemented by a BigInt, so it's odd that a BigInt can't actually be an Integer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I don't understand your comment. Can you try explaining in a different way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can't do a bitwise "not" operation on a BigInt
- at least not by the normal semantics of a bitwise "not" operation.
You can't because the you'd expect that any bits "more significant" than the most significant bit of the value would be set to 1
.
But a BigInt
has no upper bound, and thus there is no limit to the number of leading 1
bits implied by inverting its bits
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still don't understand @jemc.
"This can't be implemented by a BigInt, so it's odd that a BigInt can't actually be an Integer"
Can you explain how what you said is related to Jason's comment?
I don't understand why not being able to be implemented BigInt makes it odd that BigInt can't actually be an Integer.
I don't understand what is being said here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll copy Jason's words and insert parentheticals with my own commentary:
This (the op_not
function) can't be implemented by a BigInt
(for the reason mentioned in my previous comment)
So it's odd that a BigInt
(which is conceptually a kind of integer) can't actually be an Integer
(it cannot be a subtype of the Integer
trait, because the Integer
trait includes the op_not
function).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @jemc. I understand now.
fun bswap(): A | ||
``` | ||
|
||
`Integer` will exist above `BigInt` and as such would require defining the above methods -- however `BigInt` will be defined via other numerics so these methods could be applied recursively. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand what this means. What are "the above methods"? Those listed on the Integer trait?
"BigInt
will be defined via other numerics so these methods could be applied recursively" <-- I don't understand what this means either. There seems to be something that I am supposed to understand about "other numerics" that BigInt will be defined via (did I miss that somewhere, if yes, bringing information forward so that you don't have to hold large chunks of RFC in your head would be good). I'm also not sure what "applied recursively" means in this context.
fun atanh(): A | ||
``` | ||
|
||
`FloatingPoint` will exist above `BigFloat` and as such would require defining the above methods -- many of which are ill-defined under arbitrary precision, or are functions using C-FFI and/or LLVM intrinsics. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused. Why would we do it this way if it has these problems? This seems like an argument against doing it this way. I feel like there's some meaning here that I am missing.
I find this RFC very confusing as written. I don't understand what large sections are attempting to convey. I think I'm going to need someone to walk me through it as at the moment, I'm not really sure how to even phrase my "I don't understand" statements. |
I'd like to see this broken apart further into an RFC for where Rational and Complex fit with the existing numerics hierarchy and a separate discussion of the best way to implement "Big" numerics. There are rather complicated issues that appear to apply to only "the bigs" that I think will take some time to resolve and during that time we can probably agree to how Rational and Complex will fit in with the existing, do detailed RFCS for each and implement them before the questions around "the bigs" are resolved. |
Any thoughts on the possibility of having builtin complex primitive types? I think the RFC is proposing that all complex values will be object instances, which seems like it would be troublesome in various use-cases. For instance, I'm working on signal processing in Pony and have an actor that produces a stream of C11 standard says:
libvolk (C library for vector operations) defines the following: typedef char complex lv_8sc_t
typedef short complex lv_16sc_t
typedef long complex lv_32sc_t
typedef long long complex lv_64sc_t
typedef float complex lv_32fc_t
typedef double complex lv_64fc_t In Pony, I imagine types like:
|
@adrianboyko Since I am the author on this RFC, I will chime in.
I am not against it outright because I do not know what all that would entail. However, this RFC will be refined (when I have time) to propose the higher level aspects of what numerics we wish to introduce. Which will then be followed by specific RFCs for the structure and implementation of those specific numerics. That is to say, how complex numerics are implemented is critical to their successful adoption and definitely needs to be a part of the RFC on their implementation. |
The idea of primitive complex types was discussed a bit on the sync call. The advantage of not having an extra object allocation is a great one. To make that idea work, they would need to be in the In my opinion, this would be a good use case for struct-like value types, which I have discussed a bit in the past (and implemented in the Savi compiler for prototyping). The basic premise is that it would be a new kind of Pony type declaration that is declared just like a Having a "struct-like value type" like this would allow for avoiding the extra object allocation for the complex numeric types, as well as anything similar that others wanted to build in user-defined libraries. It also would solve the "newtype" use case discussed in #65. It would also help use cases where performance-sensitive code is currently using tuples for performance reasons where they would otherwise prefer to use classes. |
Rendered
Partially related to: