-
Notifications
You must be signed in to change notification settings - Fork 3k
fix(take): deal with non-integers #6398
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
base: master
Are you sure you want to change the base?
Conversation
src/internal/operators/take.ts
Outdated
|
||
// this condition also returns true on left-side NaN and not just if count | ||
// exceeds MAX_SAFE_INTEGER | ||
if (!(count < Number.MAX_SAFE_INTEGER + 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.
nit-pick:
if (!(count < Number.MAX_SAFE_INTEGER + 1)) { | |
if (!(count <= Number.MAX_SAFE_INTEGER)) { |
(one byte lighter 🙈 )
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, this was done intentionally, since Number.MAX_SAFE_INTEGER + somethingSmallerThanOne
is ok, (gets floored after). but it could very well be that there is no number representable between MAX_SAFE_INTEGER and MAX_SAFE_INTEGER + 1 in 64bit floats
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.
if (!(count < Number.MAX_SAFE_INTEGER + 1)) { | |
if (Number.MAX_SAFE_INTEGER < count) { |
- Inverting the check makes it less readable.
- Accounting for
Number.MAX_SAFE_INTEGER + 0.56
or whatever won't make a difference, because you can't take 0.56 of something :)
src/internal/operators/take.ts
Outdated
// this condition also returns true on left-side NaN and not just if count | ||
// exceeds MAX_SAFE_INTEGER | ||
if (!(count < Number.MAX_SAFE_INTEGER + 1)) { | ||
throw new Error(`Tried to take ${count}, but can only take at most ${Number.MAX_SAFE_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.
We have ArgumentOutOfRangeError
, which could be used here.
However, a bigger issue is this makes this fix a breaking change.
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 are a few suggestions on here.
I'm not sure about throwing the error, though. Odds are, it's a non-issue. But if we do, we need to use our existing ArgumentOutOfRangeError
, I think.
However, I am concerned about the breaking change here. We might not be able to do this until version 8.
In all honesty, the checking of MAX_SAFE_INTEGER
in general feels like overkill, because of someone really wants to take 9,007,199,254,740,991 values... at 1 per millisecond, that's more than 200,000 years. Even at 1 per nanosecond, that's like 100 days. heh.
If anything, we should treat it like we do infinity. However, my gut tells me we can just ignore it entirely and we'll never see an issue filed around it.
src/internal/operators/take.ts
Outdated
return () => EMPTY; | ||
} | ||
|
||
if (count === Number.POSITIVE_INFINITY) { |
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.
if (count === Number.POSITIVE_INFINITY) { | |
if (count === Infinity) { |
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.
is there a preference for the global Infinity? its the same value as Number.POSITIVE_INFINITY
, personally I prefer the latter
72ee6c7
to
de46b66
Compare
So we go with the other behavior:
is that ok? i reverted the PR to previous behavior and added failing test cases |
de46b66
to
7343b38
Compare
@backbone87 I think we can just not worry about MAX_SAFE_INTEGER, for the time being. Right now, the worst thing that happens is the user gets pinned to MAX_SAFE_INTEGER. Because all return !count || count <= 0
? EMPTY
: count === Infinity
? identity
: ... |
i can remove the check for MAX_SAFE_INTEGER, but as a matter of fact, the behavior is still the same as infinity, just that we keep a counter that will be stuck at some point (that will never be reached ofc) edit: if (!(count >= 1)) { // handles NaN as well
return () => EMPTY;
}
if (!(count < Number.MAX_SAFE_INTEGER + 1)) { // handles infinity as well
return identity;
} what I really mean here: we have 3 main behaviors (EMPTY, everything, counted), it doesn't matter what check we use |
7343b38
to
2dbe5ba
Compare
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 have pushed now a version, with non-erroring behavior and keeping BC for all "sane" outcomes.
changed behaviors (breaking or bugfix?):
NaN
->0
insteadofignoreElements
- non-integers > 0 & < 1 ->
0
insteadofignoreElements
- non-integers > 1 ->
floor(count)
insteadof "taking up to count and then turn intoignoreElements
"
optimized handling (non-breaking):
- counts that can not be incremented up to because of resolution limitations of 64bit floats or infinity counts just return an identity operator function instead of keeping an eventually stuck counter
// counting | ||
if (count >= Number.MAX_SAFE_INTEGER + 1) { | ||
return identity; | ||
} |
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 entire check is not needed. the behavior of operate
below will be the same as identity
if count >= Number.MAX_SAFE_INTEGER + 1
(including Infinity). we probably don't expect to hit this intentionally with high numbers, but using take(condition ? n : Infinity)
could be a valid pattern. in any way, we don't need to count seen in these cases.
Noted from Core Team discussion. We probably don't want to defend against MAX_SAFE_INTEGER, because it's so big, that it seems like it's bloat for no real benefit. |
will remove. just for confirmation: the infinity case should also not be handled in a special way? (the current MR implementation covers this in the check for MAX_SAFE_INTEGER) |
Description:
make
take
deal with non-integer counts.i presented two possibilities how to deal with them:
BREAKING CHANGE:
NaN was previously handled asactually it was treated asPOSITIVE_INFINITY
(could be considered a bug)ignoreElements
since allnext
notifications were just blockedPOSITIVE_INFINITY
, since we cant increment seen by 1 beyond MAX_SAFE_INTEGER + 1i will add test cases if the approach is agreed upon