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

Add unit test to verify Chain.zipWithIndex stack safety #3373

Merged

Conversation

dantb
Copy link
Contributor

@dantb dantb commented Mar 29, 2020

Noticed while poking around to add zipAll that zipWithIndex on Chain (also called from NonEmptyChain) isn’t tail recursive. It’ll blow the stack if the chain is built from many concats meaning we have a highly nested Append structure. E.g.

scala> List.fill(10000)(1).foldLeft(Chain(1)) { case (acc, next) => acc.concat(Chain(next)) }.zipWithIndex
java.lang.StackOverflowError
  at scala.collection.AbstractIterable.<init>(Iterable.scala:56)
  at scala.collection.AbstractSeq.<init>(Seq.scala:45)

This wasn’t caught by tests as the arbitrary instances generate only small collections - I guess this was a conscious decision?

Either way worth flagging 😄

Edit: has since been fixed in #3533 so this change only adds a unit test. Changed title to reflect that.

@travisbrown travisbrown self-requested a review March 30, 2020 12:45
travisbrown
travisbrown previously approved these changes Mar 30, 2020
Copy link
Contributor

@travisbrown travisbrown left a comment

Choose a reason for hiding this comment

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

👍, this might be my fault. Good catch, and thanks for implementing it. Do you mind adding a test?

@@ -331,7 +331,7 @@ sealed abstract class Chain[+A] {
case Singleton(a) => Singleton((a, 0))
case Append(left, right) =>
val leftSize = left.length.toInt
Append(left.zipWithIndex, right.zipWithIndex.map { case (a, i) => (a, leftSize + i) })
Wrap(left.toList.zipWithIndex ++ right.toList.zipWithIndex.map { case (a, i) => (a, leftSize + i) })
Copy link
Member

Choose a reason for hiding this comment

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

Any reason not to use a .toVector or something else that ++'s better than List here?

@dantb
Copy link
Contributor Author

dantb commented Jun 5, 2020

Sorry appalling response time on this. The arbitrary instance here is used all over the Chain tests so I thought I would add a targeted non property-based unit test, is that alright @travisbrown ?

Also I only force pushed, github making me sound sassy 💁‍♂️

@dantb dismissed travisbrown’s stale review

@barambani
Copy link
Contributor

The failure here is just styling, you probably just need to re-run +prePR and should be good to go.

@@ -333,7 +333,7 @@ sealed abstract class Chain[+A] {
case Singleton(a) => Singleton((a, 0))
case Append(left, right) =>
val leftSize = left.length.toInt
Append(left.zipWithIndex, right.zipWithIndex.map { case (a, i) => (a, leftSize + i) })
Wrap(left.toVector.zipWithIndex ++ right.toVector.zipWithIndex.map { case (a, i) => (a, leftSize + i) })
Copy link
Contributor

Choose a reason for hiding this comment

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

why not:

case a@Append(_, _) =>
  val it = a.iterator
  Wrap(it.zip(Iterator.iterate(0)(_ + 1)).toList)

this way, we only materialize one new list and leverage the existing iterator code. The code above materializes to vector (.toVector), one of them is copies again: (.map(... leftSize + i) }), then copies them (.zipWithIndex) then concats them (++).

So, there are like 6 materializations of the values. the iterator approach I suggest above does just one.

@barambani
Copy link
Contributor

I think the current master's version resolve this issue, but the test in here is still a valuable addition IMO. @dantb, I was wondering if you have some steam left to resolve the conflicts. Either way thanks a lot for your contribution.

@dantb dantb force-pushed the prevent-zipWithIndex-stackoverflow branch from f8467cd to 7f51dd7 Compare October 7, 2020 15:24
@dantb dantb changed the title Make Chain zipWithIndex stack safe Add unit test to verify Chain.zipWithIndex stack safety Oct 7, 2020
@dantb
Copy link
Contributor Author

dantb commented Oct 7, 2020

Rebased and repurposed - thanks @barambani

Copy link
Contributor

@johnynek johnynek left a comment

Choose a reason for hiding this comment

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

thanks!

@barambani
Copy link
Contributor

Thanks again.

@barambani barambani merged commit 2ddd3d2 into typelevel:master Oct 8, 2020
@dantb dantb deleted the prevent-zipWithIndex-stackoverflow branch October 27, 2020 14:36
@larsrh larsrh added this to the 2.3 milestone Oct 31, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants