-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Fix lengthCompare for indexed types #6930
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
Conversation
@@ -221,7 +221,7 @@ final class ArrayOps[A](val xs: Array[A]) extends AnyVal { | |||
* x > 0 if this.length > len | |||
* }}} | |||
*/ | |||
def lengthCompare(len: Int): Int = xs.length - len | |||
def lengthCompare(len: Int): Int = Integer.compare(xs.length, len) |
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 would prefer if this reverted to the original behavior (i.e. it returns the difference in lengths) in case anyone is depending on it.
Since we can count on length
being non-negative, it could be
val ans = xs.length - len
if (len < 0 && ans < 0) Int.MaxValue else ans
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.
the default implementation never returned the difference in lengths, though. and if they happen to know it's an IndexedSeq
(which is pretty much the only way to even maybe rely on that behaviour), they can just call .length
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 I'm with @NthPortal on this, there is no reason or need to stick to a contract that we never documented. what we documented was to only rely on the sign
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 fair enough to say that we didn't agree to obey this contract so we're abandoning it, but I don't think it's hard to preserve it for IndexedSeq
, where it worked pretty reliably (Array, ArrayBuffer, String, Vector, etc.). I guess I don't see the downside of supporting it.
/** check that lengthCompare compares values correctly */ | ||
@Test def checkLengthCompare(): Unit = { | ||
val test = underTest(size) | ||
assert(lengthCompare(test, Integer.MIN_VALUE) > 0) |
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.
Was this test previously failing?
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.
size - Int.MinValue
(also, I accidentally used Java's, oops; I'll fix that) is the same as size + Int.MaxValue + 1
, which overflows to size + Int.MinValue
or Int.MinValue + size
. Since size <= Int.MaxValue
, Int.MinValue + size <= Int.MinValue + Int.MaxValue == -1
. However, size > Int.MinValue
, so you ought to get a positive 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 actually wrote and ran the test before fixing ArrayOps
, just to be sure)
b0bd461
to
604f8d4
Compare
@@ -221,7 +221,7 @@ final class ArrayOps[A](val xs: Array[A]) extends AnyVal { | |||
* x > 0 if this.length > len | |||
* }}} | |||
*/ | |||
def lengthCompare(len: Int): Int = xs.length - len |
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.
what's the scenario where this fails? The test below has Int.MinValue, which seems impossible for a length...
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 passes in Int.MinValue
as the value of len
, which is perfectly possible. See this comment.
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.
Well OK, but why would length be negative?
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.
because someone passed a negative number in ¯\_(ツ)_/¯
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.
@isaacl - As a concrete example, suppose you have a collection xs
that you will use to pair with any overflow of ys
which is normally going to be paired with zs
. You might want to know if xs
is big enough. So you ask xs.lengthCompare(ys.length - zs.length)
.
Beyond that, even if we can't think of a sensible use case for a negative value being passed in, it still should return the logically correct result, because we can't always anticipate what someone might try to use it for.
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.
OK. Well, this function doesn't feel super useful to me, but I guess if there's no preconditions it should return correctly.
Requested change is not essential for correctness nor is it universally desired.
It looks like this is ready for merge |
thanks Nth! |
no problem! |
No description provided.