-
Notifications
You must be signed in to change notification settings - Fork 87
Add BuildFrom #3
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
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package scala.collection | ||
|
||
import scala.collection.generic.CanBuildFrom | ||
|
||
/** Builds a collection of type `C` from elements of type `A` when a source collection of type `From` is available. | ||
* Implicit instances of `BuildFrom` are available for all collection types. | ||
* | ||
* @tparam From Type of source collection | ||
* @tparam A Type of elements (e.g. `Int`, `Boolean`, etc.) | ||
* @tparam C Type of collection (e.g. `List[Int]`, `TreeMap[Int, String]`, etc.) | ||
*/ | ||
trait BuildFrom[-From, -A, +C] extends Any { | ||
|
||
def fromSpecificIterable(from: From)(it: Iterable[A]): C | ||
|
||
/** Get a Builder for the collection. For non-strict collection types this will use an intermediate buffer. | ||
* Building collections with `fromSpecificIterable` is preferred because it can be lazy for lazy collections. */ | ||
def newBuilder(from: From): mutable.Builder[A, C] | ||
|
||
@deprecated("Use newBuilder() instead of apply()", "2.13.0") | ||
@`inline` def apply(from: From): mutable.Builder[A, C] = newBuilder(from) | ||
|
||
} | ||
|
||
object BuildFrom { | ||
|
||
// Implicit instance derived from an implicit CanBuildFrom instance | ||
implicit def fromCanBuildFrom[From, A, C](implicit cbf: CanBuildFrom[From, A, C]): BuildFrom[From, A, C] = | ||
new BuildFrom[From, A, C] { | ||
def fromSpecificIterable(from: From)(it: Iterable[A]): C = (cbf(from) ++= it).result() | ||
def newBuilder(from: From): mutable.Builder[A, C] = cbf(from) | ||
} | ||
|
||
// Implicit conversion derived from an implicit conversion to CanBuildFrom | ||
implicit def fromCanBuildFromConversion[X, From, A, C](x: X)(implicit toCanBuildFrom: X => CanBuildFrom[From, A, C]): BuildFrom[From, A, C] = | ||
fromCanBuildFrom(toCanBuildFrom(x)) | ||
|
||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
package scala.collection | ||
|
||
import org.junit.Test | ||
|
||
import scala.collection.mutable.{ArrayBuffer, Builder, ListBuffer} | ||
import scala.collection.immutable.{HashMap, List, TreeMap, TreeSet} | ||
import scala.collection.compat._ | ||
|
||
// Tests copied from the 2.13 scala-library | ||
class BuildFromTest { | ||
|
||
// Using BuildFrom to abstract over both and also allow building arbitrary collection types | ||
def optionSequence2[CC[X] <: Iterable[X], A, To](xs: CC[Option[A]])(implicit bf: BuildFrom[CC[Option[A]], A, To]): Option[To] = | ||
xs.foldLeft[Option[Builder[A, To]]](Some(bf.newBuilder(xs))) { | ||
case (Some(builder), Some(a)) => Some(builder += a) | ||
case _ => None | ||
}.map(_.result()) | ||
|
||
// Using dependent types: | ||
def optionSequence3[A, To](xs: Iterable[Option[A]])(implicit bf: BuildFrom[xs.type, A, To]): Option[To] = | ||
xs.foldLeft[Option[Builder[A, To]]](Some(bf.newBuilder(xs))) { | ||
case (Some(builder), Some(a)) => Some(builder += a) | ||
case _ => None | ||
}.map(_.result()) | ||
|
||
def eitherSequence[A, B, To](xs: Iterable[Either[A, B]])(implicit bf: BuildFrom[xs.type, B, To]): Either[A, To] = | ||
xs.foldLeft[Either[A, Builder[B, To]]](Right(bf.newBuilder(xs))) { | ||
case (Right(builder), Right(b)) => Right(builder += b) | ||
case (Left(a) , _) => Left(a) | ||
case (_ , Left(a)) => Left(a) | ||
}.right.map(_.result()) | ||
|
||
@Test | ||
def optionSequence2Test: Unit = { | ||
val xs1 = List(Some(1), None, Some(2)) | ||
val o1 = optionSequence2(xs1) | ||
val o1t: Option[List[Int]] = o1 | ||
|
||
val xs2 = TreeSet(Some("foo"), Some("bar"), None) | ||
val o2 = optionSequence2(xs2) | ||
// Not working: the resolved implicit BuildFrom results in a SortedSet instead of a TreeSet | ||
// val o2t: Option[TreeSet[String]] = o2 | ||
val o2t: Option[SortedSet[String]] = o2 | ||
|
||
// Breakout-like use case from https://github.com/scala/scala/pull/5233: | ||
val xs4 = List[Option[(Int, String)]](Some((1 -> "a")), Some((2 -> "b"))) | ||
val o4 = optionSequence2(xs4)(TreeMap) | ||
val o4t: Option[TreeMap[Int, String]] = o4 | ||
} | ||
|
||
@Test | ||
def optionSequence3Test: Unit = { | ||
val xs1 = List(Some(1), None, Some(2)) | ||
val o1 = optionSequence3(xs1) | ||
val o1t: Option[List[Int]] = o1 | ||
|
||
val xs2 = TreeSet(Some("foo"), Some("bar"), None) | ||
val o2 = optionSequence3(xs2) | ||
// Not working: the resolved implicit BuildFrom results in a SortedSet instead of a TreeSet | ||
// val o2t: Option[TreeSet[String]] = o2 | ||
val o2t: Option[SortedSet[String]] = o2 | ||
|
||
// Breakout-like use case from https://github.com/scala/scala/pull/5233: | ||
val xs4 = List[Option[(Int, String)]](Some((1 -> "a")), Some((2 -> "b"))) | ||
val o4 = optionSequence3(xs4)(TreeMap) // same syntax as in `.to` | ||
val o4t: Option[TreeMap[Int, String]] = o4 | ||
} | ||
|
||
@Test | ||
def eitherSequenceTest: Unit = { | ||
val xs3 = ListBuffer(Right("foo"), Left(0), Right("bar")) | ||
val e1 = eitherSequence(xs3) | ||
val e1t: Either[Int, ListBuffer[String]] = e1 | ||
} | ||
|
||
// From https://github.com/scala/collection-strawman/issues/44 | ||
def flatCollect[A, B, To](coll: Iterable[A])(f: PartialFunction[A, IterableOnce[B]]) | ||
(implicit bf: BuildFrom[coll.type, B, To]): To = { | ||
val builder = bf.newBuilder(coll) | ||
for (a <- coll) { | ||
if (f.isDefinedAt(a)) builder ++= f(a) | ||
} | ||
builder.result() | ||
} | ||
|
||
def mapSplit[A, B, C, ToL, ToR](coll: Iterable[A])(f: A => Either[B, C]) | ||
(implicit bfLeft: BuildFrom[coll.type, B, ToL], bfRight: BuildFrom[coll.type, C, ToR]): (ToL, ToR) = { | ||
val left = bfLeft.newBuilder(coll) | ||
val right = bfRight.newBuilder(coll) | ||
for (a <- coll) | ||
f(a).fold(left.+=, right.+=) | ||
(left.result(), right.result()) | ||
} | ||
|
||
@Test | ||
def flatCollectTest: Unit = { | ||
val xs1 = List(1, 2, 3) | ||
val xs2 = flatCollect(xs1) { case 2 => ArrayBuffer("foo", "bar") } | ||
val xs3: List[String] = xs2 | ||
|
||
val xs4 = TreeMap((1, "1"), (2, "2")) | ||
val xs5 = flatCollect(xs4) { case (2, v) => List((v, v)) } | ||
val xs6: TreeMap[String, String] = xs5 | ||
|
||
val xs7 = HashMap((1, "1"), (2, "2")) | ||
val xs8 = flatCollect(xs7) { case (2, v) => List((v, v)) } | ||
val xs9: HashMap[String, String] = xs8 | ||
|
||
val xs10 = TreeSet(1, 2, 3) | ||
val xs11 = flatCollect(xs10) { case 2 => List("foo", "bar") } | ||
// Not working: the resolved implicit BuildFrom results in a SortedSet instead of a TreeSet | ||
// val xs12: TreeSet[String] = xs11 | ||
val xs12: SortedSet[String] = xs11 | ||
} | ||
|
||
@Test | ||
def mapSplitTest: Unit = { | ||
val xs1 = List(1, 2, 3) | ||
val (xs2, xs3) = mapSplit(xs1)(x => if (x % 2 == 0) Left(x) else Right(x.toString)) | ||
val xs4: List[Int] = xs2 | ||
val xs5: List[String] = xs3 | ||
|
||
val xs6 = TreeMap((1, "1"), (2, "2")) | ||
val (xs7, xs8) = mapSplit(xs6) { case (k, v) => Left[(String, Int), (Int, Boolean)]((v, k)) } | ||
val xs9: TreeMap[String, Int] = xs7 | ||
val xs10: TreeMap[Int, Boolean] = xs8 | ||
} | ||
|
||
implicitly[BuildFrom[String, Char, String]] | ||
implicitly[BuildFrom[Array[Int], Char, Array[Char]]] | ||
implicitly[BuildFrom[BitSet, Int, BitSet]] | ||
implicitly[BuildFrom[immutable.BitSet, Int, immutable.BitSet]] | ||
implicitly[BuildFrom[mutable.BitSet, Int, mutable.BitSet]] | ||
|
||
// Check that collection companions can implicitly be converted to a `BuildFrom` instance | ||
Iterable: BuildFrom[_, Int, Iterable[Int]] | ||
Map: BuildFrom[_, (Int, String), Map[Int, String]] | ||
SortedSet: BuildFrom[_, Int, SortedSet[Int]] | ||
SortedMap: BuildFrom[_, (Int, String), SortedMap[Int, String]] | ||
} |
2 changes: 2 additions & 0 deletions
2
src/test/scala/CollectionTest.scala → ...est/scala/collection/CollectionTest.scala
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
package collection | ||
|
||
import java.util | ||
|
||
import org.junit.Test | ||
|
5 changes: 3 additions & 2 deletions
5
src/test/scala/ImmutableArrayTest.scala → ...scala/collection/ImmutableArrayTest.scala
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 point of putting the tests into this package?
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.
Just to not use the empty package.