Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Fix lengthCompare for indexed types
  • Loading branch information
NthPortal committed Jul 13, 2018
commit 604f8d41c972656529969376a1c35bb9025c2053
2 changes: 1 addition & 1 deletion src/library/scala/collection/ArrayOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

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...

Copy link
Contributor Author

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.

Copy link
Contributor

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?

Copy link
Contributor Author

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 ¯\_(ツ)_/¯

Copy link
Contributor

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.

Copy link
Contributor

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.

def lengthCompare(len: Int): Int = Integer.compare(xs.length, len)
Copy link
Contributor

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

Copy link
Contributor Author

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

Copy link
Member

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

Copy link
Contributor

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.


/** Selects an interval of elements. The returned array is made up
* of all elements `x` which satisfy the invariant:
Expand Down
2 changes: 1 addition & 1 deletion src/library/scala/collection/IndexedSeq.scala
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ trait IndexedSeqOps[+A, +CC[_], +C] extends Any with SeqOps[A, CC, C] { self =>

override def last: A = apply(length - 1)

override def lengthCompare(len: Int): Int = length - len
override def lengthCompare(len: Int): Int = Integer.compare(length, len)

final override def knownSize: Int = length

Expand Down
2 changes: 2 additions & 0 deletions src/library/scala/collection/StringOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ final class StringOps(private val s: String) extends AnyVal {
/** Get the char at the specified index. */
@`inline` def apply(i: Int): Char = s.charAt(i)

def lengthCompare(len: Int): Int = Integer.compare(s.length, len)

/** Builds a new collection by applying a function to all chars of this String.
*
* @param f the function to apply to each char.
Expand Down
2 changes: 0 additions & 2 deletions src/library/scala/collection/immutable/Vector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,6 @@ final class Vector[+A] private[immutable] (private[collection] val startIndex: I

def length: Int = endIndex - startIndex

override def lengthCompare(len: Int): Int = length - len

private[collection] def initIterator[B >: A](s: VectorIterator[B]): Unit = {
s.initFrom(this)
if (dirty) s.stabilize(focus)
Expand Down
18 changes: 18 additions & 0 deletions test/junit/scala/collection/IndexedSeqTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ abstract class IndexedTest[T, E] {
}
}

/** check that lengthCompare compares values correctly */
@Test def checkLengthCompare(): Unit = {
val test = underTest(size)
assert(lengthCompare(test, Int.MinValue) > 0)
}

/**
* check simple equallity of the initial data.
* More a test of the infra that we use in this est than a full test of equallity
Expand Down Expand Up @@ -231,6 +237,8 @@ abstract class IndexedTest[T, E] {
//the length of underTest
def length(underTest: T): Int

def lengthCompare(underTest: T, len: Int): Int

//the value at index i of underTest
def get(underTest: T, i: Int): E

Expand Down Expand Up @@ -316,6 +324,8 @@ package IndexedTestImpl {
val TYPE: Class[_]) extends IndexedTest[Array[E], E]{
override final def length(underTest: Array[E]) = underTest.length

override final def lengthCompare(underTest: Array[E], len: Int): Int = underTest.lengthCompare(len)

override def get(underTest: Array[E], i: Int) = underTest(i)

override def slice(underTest: Array[E], from: Int, to: Int) = underTest.slice(from, to)
Expand Down Expand Up @@ -347,6 +357,8 @@ package IndexedTestImpl {
import mutable.ArraySeq
override final def length(underTest: ArraySeq[E]) = underTest.length

override final def lengthCompare(underTest: ArraySeq[E], len: Int): Int = underTest.lengthCompare(len)

override def get(underTest: ArraySeq[E], i: Int) = underTest(i)

override def slice(underTest: ArraySeq[E], from: Int, to: Int) = underTest.slice(from, to)
Expand Down Expand Up @@ -377,6 +389,8 @@ package IndexedTestImpl {
abstract class MutableIndexedSeqTest[T <: mutable.Seq[E], E] extends IndexedTest[T, E] with DataProvider[E]{
override final def length(underTest: T) = underTest.length

override final def lengthCompare(underTest: T, len: Int): Int = underTest.lengthCompare(len)

override def get(underTest: T, i: Int) = underTest(i)

override def slice(underTest: T, from: Int, to: Int) = underTest.slice(from, to).asInstanceOf[T]
Expand Down Expand Up @@ -407,6 +421,8 @@ package IndexedTestImpl {
abstract class ImmutableIndexedSeqTest[T <: SeqOps[E, Seq, T], E] extends IndexedTest[T, E] with DataProvider[E] {
override final def length(underTest: T) = underTest.length

override final def lengthCompare(underTest: T, len: Int): Int = underTest.lengthCompare(len)

override def get(underTest: T, i: Int) = underTest(i)

override def slice(underTest: T, from: Int, to: Int) = underTest.slice(from, to)
Expand All @@ -428,6 +444,8 @@ package IndexedTestImpl {
abstract class StringOpsBaseTest extends IndexedTest[StringOps, Char] with DataProvider[Char] {
override final def length(underTest: StringOps) = underTest.size

override final def lengthCompare(underTest: StringOps, len: Int): Int = underTest.lengthCompare(len)

override def get(underTest: StringOps, i: Int) = underTest(i)

override def slice(underTest: StringOps, from: Int, to: Int) = underTest.slice(from, to)
Expand Down