-
Notifications
You must be signed in to change notification settings - Fork 448
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
Unified operators #7057
Unified operators #7057
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as resolved.
This comment was marked as resolved.
@cometkim you waiting on anything from any of us on this one? |
No, I just took a break from coding over the Korean holidays. |
Enjoy your holidays! |
7d0dcee
to
6eaf032
Compare
604621b
to
f8a673e
Compare
It now works for basic arithmetic operators |
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.
match entry with | ||
| Some {specialization} -> ( | ||
match specialization with | ||
| {int} |
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 was wondering if the when
clause in this case is complete. But I guess this case is just unnecessary and can be removes as it is already expressed as the last default case?
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 always takes precedence int over other types. (Rule 1-2) The last default case is a fallback strategy. (Rule 3)
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.
Can you give an example of any change after removing this first case?
All the other cases seem to have incompatible when clauses, so it would fall back to the last case no matter what.
Unless I'm missing something.
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 are right. There is no logical difference in the behavior after removing that case. I was just thinking of the computational difference. In the existing codebase, I assume the first case is hit the most frequently (since it was originally int-only).
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.
It's just a detail anyway. Not much difference. Whatever you think is best.
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.
Yeah, it probably doesn't matter, but I'll leave it as is because it seems easier to understand the intent.
if String.length prim_name > 0 && prim_name.[0] = '%' then | ||
raise (Error (loc, Unknown_builtin_primitive prim_name)); | ||
Pccall prim | ||
| [arg1] | [arg1; _] -> translate_unified_ops prim env arg1.exp_type |
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.
why also [arg1]
with only 1 argument?
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.
There is a couple of unary primitive to support, %pos
and %neg
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.
Yes I noticed all the other functions don't have unaries, so if they're planned for this PR great.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
compiler/ml/typecore.ml
Outdated
let rhs = type_exp env rhs_expr in | ||
let rhs_type = expand_head env rhs.exp_type in | ||
let lhs, rhs, result_type = | ||
(* rule 1. *) |
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.
Some overall comment above should summarise what rule1, rule2, rule3 are, before we get this far into the code.
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 can leave a link here to read the unified_ops.ml
module first.
Or would you prefer to duplicate the same explanation 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.
Link sounds perfect.
Or should the code move too? Not sure.
Just wondering why the description is in one file and the code in an other. Perhaps dependencies or similarity to other code.
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.
Just wondering why the description is in one file and the code in an other. Perhaps dependencies or similarity to other code.
I wanted to keep everything in a single file if possible. Unified_ops
should be the only module that manages the list of supported operators.
But I used a utility from typecore.ml
like type_exp
. It isn't easy to replicate as it is defined recursively.
seems a pushing it a bit far -- but just reporting here what a quick search returned |
Agreed, looks great! 👏 |
Sounds like an abbreviation of I'm not sure if that will be useful or cleaner. |
I haven't added
|
For other operators, I need to implement/rework additional primitives. It seems better to merge this first and then do it later on. |
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.
Sounds great!
Would you add a change log before merging?
We’ll also need to set documentation tasks for this.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
specialization: specialization; | ||
} | ||
|
||
let builtin x = Primitive_modules.pervasives ^ "." ^ x |
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.
Ohhhhh right it's pervasives_mini, not pervasive. tricky 😅
dff2e3f
to
1ef4469
Compare
changelog added |
documentation issue opened rescript-lang/rescript-lang.org#934 |
Trying to solve #6477, but without implicit open
Rationale
As explained in #6477, we have the "infix explosion" problem. To relax it, I added some specialization to both type inference and primitive inspired by F#
https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/symbol-and-operator-reference/arithmetic-operators#operators-and-type-inference
The idea is, to extend the type inference rules for primitive operations defined in Pervasives to specialize them for predefined types but fall back to
int
if they cannot be inferred.This doesn't make a breaking change since the existing operations are already defined on
int
.However, we can introduce them as unified operators for the new codebase. They are still safe, as they are always specialized and not polymorphic.
Specialized operators:
~+
%plus
int
,float
,bigint
~-
%neg
int
,float
,bigint
+
%add
int
,float
,bigint
,string
-
%sub
int
,float
,bigint
*
%mul
int
,float
,bigint
/
%div
int
,float
,bigint
mod
%mod
int
,float
,bigint
**
%exp
int
,float
,bigint
&
%bitand
(formerly%land
)int
,bigint
|
%bitor
(formerly%lor
)int
,bigint
^
%bitxor
(formerly%lxor
)int
,bigint
~
%bitnot
(formerly%lnot
)int
,bigint
<<
%lsl
int
,bigint
>>
%asr
int
,bigint
>>>
%lsr
int
abs
%abs
int
,float
,bigint
min
%min
int
,float
,bigint
,string
max
%max
int
,float
,bigint
,string
Side note
This PR doesn't attempt to add custom ops or polymorphic ops, but that's the specialized custom ops already possible by opening a module.
Polymorphic ops should be deprecated. They are unpredictable and inefficient, as explained in #7031. So, in future versions, comparisons will behave consistently with this change. It will be a breaking change, but we don't have a better alternative yet.