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

NaN can be used as a key in Map #6301

Merged
merged 19 commits into from
Apr 20, 2023
Merged

Conversation

Akirathan
Copy link
Member

@Akirathan Akirathan commented Apr 17, 2023

Fixes #5833 #6065

Pull Request Description

Number.nan can be used as a key in Map. This PR basically implements the support for JavaScript's Same Value Zero Equality so that Number.nan can be used as a key in Map.

Important Notes

  • For NaN, it holds that Meta.is_same_object Number.nan Number.nan, and Number.nan != Number.nan - inspired by JS spec.
  • Meta.is_same_object x y implies Any.== x y, except for Number.nan.

Checklist

Please ensure that the following checklist has been satisfied before submitting the PR:

  • The documentation has been updated, if necessary.
  • Screenshots/screencasts have been attached, if there are any visual changes. For interactive or animated visual changes, a screencast is preferred.
  • All code follows the
    Scala,
    Java,
    and
    Rust
    style guides. In case you are using a language not listed above, follow the Rust style guide.
  • All code has been tested:
    • Unit tests have been written where possible.
    • If GUI codebase was changed, the GUI was tested when built using ./run ide build.

@Akirathan Akirathan linked an issue Apr 17, 2023 that may be closed by this pull request
@mwu-tow
Copy link
Contributor

mwu-tow commented Apr 17, 2023

@Akirathan

Number.nan can be used as a key in Map.

Why do we want this? For me NaN is the value that is not equal to itself. That by definition kind of prevents it from being useful as a key in the map.
Even if we wanted this, I'd rather expect some kind of byte-representation-comparison rather than just NaN==NaN assumption. There are different values (byte-wise) that are considered to be a 'NaN'. Do we want to disregard the differences?

@Akirathan Akirathan self-assigned this Apr 17, 2023
@Akirathan
Copy link
Member Author

@Akirathan

Number.nan can be used as a key in Map.

Why do we want this? For me NaN is the value that is not equal to itself. That by definition kind of prevents it from being useful as a key in the map. Even if we wanted this, I'd rather expect some kind of byte-representation-comparison rather than just NaN==NaN assumption. There are different values (byte-wise) that are considered to be a 'NaN'. Do we want to disregard the differences?

As noted in the issue description #5833, we want to comply with JavaScript's same-value-zero equality. The reason for that is because we want every value in Enso, including polyglot values, to have some hash code, and thus be used as a key in the map.

Map.singleton (Map.singleton Number.nan 1) 42 . keys . at 0 . get Number.nan . should_equal 1
Map.singleton (Map.singleton Number.nan 1) 42 . at (Map.singleton Number.nan 1) . should_equal 42

Test.specify "should support atoms with custom comparators that violate reflexivity as keys" <|
Copy link
Member Author

Choose a reason for hiding this comment

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

This test was copied from the issue description

@Akirathan Akirathan linked an issue Apr 17, 2023 that may be closed by this pull request
@Akirathan Akirathan added the CI: No changelog needed Do not require a changelog entry for this PR. label Apr 18, 2023
@Akirathan Akirathan marked this pull request as ready for review April 18, 2023 09:36
Atom other,
@Cached EqualsAtomNode equalsNode,
@Cached IsSameObjectNode isSameObjectNode) {
return isSameObjectNode.execute(self, other) || equalsNode.execute(self, other);
Copy link
Member

Choose a reason for hiding this comment

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

This is the fix for #6065, right?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, #6065 is addressed in these commits:

@@ -23,6 +25,15 @@ public static IsSameObjectNode build() {

public abstract boolean execute(@AcceptsError Object left, @AcceptsError Object right);

@Specialization
boolean isSameDouble(double left, double right) {
if (Double.isNaN(left) && Double.isNaN(right)) {
Copy link
Member

Choose a reason for hiding this comment

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

This is the handling of NaN as being "same".


Test.specify "type_of Nothing is Nothing" <|
Meta.type_of Nothing . should_equal Nothing

Test.specify "NaN and NaN should be the same object" <|
Meta.is_same_object Number.nan Number.nan . should_be_true
(Number.nan == Number.nan) . should_be_false
Copy link
Member

Choose a reason for hiding this comment

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

nan != nan, but Meta.is_same_object gives true. OK.

Copy link
Collaborator

@hubertp hubertp left a comment

Choose a reason for hiding this comment

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

Do we want/need any tests for polyglot handling of NaNs?

Comment on lines +657 to +667
Test.specify "should be able to handle distinct on different primitive values" <|
alter [1, "a"] . distinct . should_equal [1, "a"]
alter ["a", 1] . distinct . should_equal ["a", 1]
alter [Nothing, Nothing] . distinct . should_equal [Nothing]
alter [Number.nan, Number.nan] . distinct . to_text . should_equal "[NaN]"
alter [Nothing, Number.nan, Nothing, Number.nan] . distinct . to_text . should_equal "[Nothing, NaN]"
alter [1, Nothing] . distinct . should_equal [1, Nothing]
alter [Nothing, 1, Nothing] . distinct . should_equal [Nothing, 1]
alter [1, Number.nan] . distinct . to_text . should_equal "[1, NaN]"
alter [Number.nan, 1, Number.nan] . distinct . to_text . should_equal "[NaN, 1]"
alter [1, Number.nan, 1] . distinct . to_text . should_equal "[1, NaN]"
Copy link
Member

Choose a reason for hiding this comment

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

How does distinct work with other non-reflexive values, like My_Nan? Can we get a test for it too?

Copy link
Member Author

Choose a reason for hiding this comment

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

Added in 6bae728

Copy link
Member

@radeusgd radeusgd left a comment

Choose a reason for hiding this comment

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

Looks good, I'd just appreciate a test for My_Nan in distinct.

This also should at some point work consistently in Table.aggregate - Aggregate_Column.Group_By. I guess no need to add these tests now, but maybe we should add a note to #6292 to not forget about it? It is quite a brittle feature and easy to get wrong so it will be good to have tests for this case once available.

@Akirathan Akirathan added the CI: Ready to merge This PR is eligible for automatic merge label Apr 20, 2023
@mergify mergify bot merged commit 4076a64 into develop Apr 20, 2023
@mergify mergify bot deleted the wip/akirathan/nan-in-map-5833 branch April 20, 2023 09:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CI: No changelog needed Do not require a changelog entry for this PR. CI: Ready to merge This PR is eligible for automatic merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Any.== should check for Meta.is_same_object as the first step NaN cannot be used as a key in Map
5 participants