Skip to content

Avoid inheriting suboptimal concat implementation for immutable collections #7166

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

Closed
wants to merge 1 commit into from

Conversation

odd
Copy link
Contributor

@odd odd commented Sep 3, 2018

Fixes scala/bug#11128 by separating the StrictOptimizedOps-family of traits into one set for mutable collections (which continue to use the builder based concat/appended/appendedAll/prepended/prependedAll/padTo implementations) and another set for the immutable collections (which does not have the builder based implementations since they have implementations inherited from e.g. immutable.SetOps that are more optimal). The root StrictOptimizedIterableOps-trait is still left in the collection package and is inherited by the specific implementations in the mutable/immutable subsets.

@scala-jenkins scala-jenkins added this to the 2.13.0-RC1 milestone Sep 3, 2018
@odd odd force-pushed the immutable-concat-performance branch from 397f6f0 to f9ee21e Compare September 10, 2018 21:53
@lrytz
Copy link
Member

lrytz commented Oct 1, 2018

Needs a rebase, and updates to some tests.

Looks good to me, but probably @szeiger should review this too.

@lrytz lrytz requested a review from szeiger October 1, 2018 14:03
@odd odd force-pushed the immutable-concat-performance branch 2 times, most recently from ac31fbe to b49a98e Compare October 5, 2018 13:33
@odd odd force-pushed the immutable-concat-performance branch from b49a98e to a635a2a Compare October 5, 2018 14:03
@odd
Copy link
Contributor Author

odd commented Oct 6, 2018

/rebuild

@SethTisue SethTisue added the library:collections PRs involving changes to the standard collection library label Nov 8, 2018
@szeiger
Copy link
Contributor

szeiger commented Nov 23, 2018

Pinging @odd. This still has some test failures and also needs to be rebased.

@odd
Copy link
Contributor Author

odd commented Nov 23, 2018

@szeiger The purpose of this PR was to avoid the performance degradation introduced by the StrictOptimizedOps-family of traits for persistent immutable collections that could offer better implementations by exploiting the persistent nature of their inner structure (i.e. avoiding the need to always build a new instance from scratch). To get to that goal I tried to separate thecollection.StrictOptimizedOps-family into mutable and immutable sub-families which were only connected at the base in collection.StrictOptimizedIterableOps, but this lead to strange lubs with structural types in some cases (e.g. the lub of List(List[Int], ListBuffer[Int])).

Instead I then started looking at the actual cases where a sub-optimal implementation from a StrictOptimized-trait was overriding a more appropriate implementation, which has so far resulted in PR #7454 and PR #7455. There might be more such cases however.

So, in conclusion, this PR might be a bit too far reaching (if the lubs containing structural types are seen as something to avoid), or perhaps it is not going far enough (by removing collection.StrictOptimizedIterableOps and instead adding a mutable and an immutable version, the StrictOptimizedOps-families become unrelated; this will lead to some code duplication but the lubs would probably be simpler). Any ideas for the best way of going forward are most welcome.

@szeiger
Copy link
Contributor

szeiger commented Dec 11, 2018

Right, the lubs are ugly:

scala> def lub[A, B >: A](a: A, b: B): B = null.asInstanceOf[B]
lub: [A, B >: A](a: A, b: B)B

scala> :type lub(List(42), collection.mutable.ListBuffer(42))
scala.collection.AbstractSeq[Int] with scala.collection.StrictOptimizedIterableOps[Int,[_]scala.collection.AbstractSeq[_] with scala.collection.StrictOptimizedIterableOps[_,[_]scala.collection.AbstractSeq[_] with scala.collection.StrictOptimizedIterableOps[_,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[_]]{def appendedAll[B >: _](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: _](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: _](elem: B): scala.collection.AbstractSeq[B]},scala.collection.AbstractSeq[_] with scala.collection.StrictOptimizedIterableOps[_,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[_]]{def appendedAll[B >: _](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: _](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: _](elem: B): scala.collection.AbstractSeq[B]}]{def appendedAll[B >: _](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B] with scala.collection.StrictOptimizedIterableOps[B,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[B]]{def appendedAll[B >: B](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: B](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: B](elem: B): scala.collection.AbstractSeq[B]}; def prependedAll[B >: _](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B] with scala.collection.StrictOptimizedIterableOps[B,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[B]]{def appendedAll[B >: B](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: B](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: B](elem: B): scala.collection.AbstractSeq[B]}; def prepended[B >: _](elem: B): scala.collection.AbstractSeq[B] with scala.collection.StrictOptimizedIterableOps[B,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[B]]{def appendedAll[B >: B](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: B](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: B](elem: B): scala.collection.AbstractSeq[B]}},scala.collection.AbstractSeq[Int] with scala.collection.StrictOptimizedIterableOps[Int,[_]scala.collection.AbstractSeq[_] with scala.collection.StrictOptimizedIterableOps[_,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[_]]{def appendedAll[B >: _](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: _](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: _](elem: B): scala.collection.AbstractSeq[B]},scala.collection.AbstractSeq[Int] with scala.collection.StrictOptimizedIterableOps[Int,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[Int]]{def appendedAll[B >: Int](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: Int](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: Int](elem: B): scala.collection.AbstractSeq[B]}]{def appendedAll[B >: Int](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B] with scala.collection.StrictOptimizedIterableOps[B,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[B]]{def appendedAll[B >: B](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: B](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: B](elem: B): scala.collection.AbstractSeq[B]}; def prependedAll[B >: Int](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B] with scala.collection.StrictOptimizedIterableOps[B,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[B]]{def appendedAll[B >: B](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: B](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: B](elem: B): scala.collection.AbstractSeq[B]}; def prepended[B >: Int](elem: B): scala.collection.AbstractSeq[B] with scala.collection.StrictOptimizedIterableOps[B,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[B]]{def appendedAll[B >: B](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: B](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: B](elem: B): scala.collection.AbstractSeq[B]}}]{def appendedAll[B >: Int](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B] with scala.collection.StrictOptimizedIterableOps[B,[_]scala.collection.AbstractSeq[_] with scala.collection.StrictOptimizedIterableOps[_,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[_]]{def appendedAll[B >: _](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: _](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: _](elem: B): scala.collection.AbstractSeq[B]},scala.collection.AbstractSeq[B] with scala.collection.StrictOptimizedIterableOps[B,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[B]]{def appendedAll[B >: B](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: B](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: B](elem: B): scala.collection.AbstractSeq[B]}]{def appendedAll[B >: B](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B] with scala.collection.StrictOptimizedIterableOps[B,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[B]]{def appendedAll[B >: B](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: B](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: B](elem: B): scala.collection.AbstractSeq[B]}; def prependedAll[B >: B](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B] with scala.collection.StrictOptimizedIterableOps[B,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[B]]{def appendedAll[B >: B](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: B](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: B](elem: B): scala.collection.AbstractSeq[B]}; def prepended[B >: B](elem: B): scala.collection.AbstractSeq[B] with scala.collection.StrictOptimizedIterableOps[B,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[B]]{def appendedAll[B >: B](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: B](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: B](elem: B): scala.collection.AbstractSeq[B]}}; def prependedAll[B >: Int](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B] with scala.collection.StrictOptimizedIterableOps[B,[_]scala.collection.AbstractSeq[_] with scala.collection.StrictOptimizedIterableOps[_,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[_]]{def appendedAll[B >: _](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: _](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: _](elem: B): scala.collection.AbstractSeq[B]},scala.collection.AbstractSeq[B] with scala.collection.StrictOptimizedIterableOps[B,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[B]]{def appendedAll[B >: B](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: B](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: B](elem: B): scala.collection.AbstractSeq[B]}]{def appendedAll[B >: B](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B] with scala.collection.StrictOptimizedIterableOps[B,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[B]]{def appendedAll[B >: B](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: B](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: B](elem: B): scala.collection.AbstractSeq[B]}; def prependedAll[B >: B](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B] with scala.collection.StrictOptimizedIterableOps[B,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[B]]{def appendedAll[B >: B](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: B](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: B](elem: B): scala.collection.AbstractSeq[B]}; def prepended[B >: B](elem: B): scala.collection.AbstractSeq[B] with scala.collection.StrictOptimizedIterableOps[B,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[B]]{def appendedAll[B >: B](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: B](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: B](elem: B): scala.collection.AbstractSeq[B]}}; def prepended[B >: Int](elem: B): scala.collection.AbstractSeq[B] with scala.collection.StrictOptimizedIterableOps[B,[_]scala.collection.AbstractSeq[_] with scala.collection.StrictOptimizedIterableOps[_,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[_]]{def appendedAll[B >: _](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: _](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: _](elem: B): scala.collection.AbstractSeq[B]},scala.collection.AbstractSeq[B] with scala.collection.StrictOptimizedIterableOps[B,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[B]]{def appendedAll[B >: B](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: B](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: B](elem: B): scala.collection.AbstractSeq[B]}]{def appendedAll[B >: B](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B] with scala.collection.StrictOptimizedIterableOps[B,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[B]]{def appendedAll[B >: B](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: B](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: B](elem: B): scala.collection.AbstractSeq[B]}; def prependedAll[B >: B](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B] with scala.collection.StrictOptimizedIterableOps[B,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[B]]{def appendedAll[B >: B](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: B](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: B](elem: B): scala.collection.AbstractSeq[B]}; def prepended[B >: B](elem: B): scala.collection.AbstractSeq[B] with scala.collection.StrictOptimizedIterableOps[B,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[B]]{def appendedAll[B >: B](suffix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prependedAll[B >: B](prefix: scala.collection.IterableOnce[B]): scala.collection.AbstractSeq[B]; def prepended[B >: B](elem: B): scala.collection.AbstractSeq[B]}}}

This is what you get without these changes:

scala> :type lub(List(42), collection.mutable.ListBuffer(42))
scala.collection.AbstractSeq[Int] with scala.collection.StrictOptimizedSeqOps[Int,[_]scala.collection.AbstractSeq[_] with scala.collection.StrictOptimizedSeqOps[_,[_]scala.collection.AbstractSeq[_] with scala.collection.StrictOptimizedSeqOps[_,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[_]],scala.collection.AbstractSeq[_] with scala.collection.StrictOptimizedSeqOps[_,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[_]]],scala.collection.AbstractSeq[Int] with scala.collection.StrictOptimizedSeqOps[Int,[_]scala.collection.AbstractSeq[_] with scala.collection.StrictOptimizedSeqOps[_,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[_]],scala.collection.AbstractSeq[Int] with scala.collection.StrictOptimizedSeqOps[Int,[_]scala.collection.AbstractSeq[_],scala.collection.AbstractSeq[Int]]]]

Is there a ticket for this or some discussion why they occur? The traits contain only overrides and List and ListBuffer already contain several shared and several non-shared traits. It's not clear to me why this change would lead to structural types in lubs.

@lrytz
Copy link
Member

lrytz commented Dec 11, 2018

There's scala/bug#10908

@szeiger
Copy link
Contributor

szeiger commented Jan 2, 2019

I'm closing this because we don't want to introduce more ugly lubs and there's already a ticket for that (and alternative fixes for the issue at hand).

@szeiger szeiger closed this Jan 2, 2019
@SethTisue SethTisue removed this from the 2.13.0-RC1 milestone Jan 2, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
library:collections PRs involving changes to the standard collection library
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants