Skip to content

Commit 19deff5

Browse files
committed
Initial version
0 parents  commit 19deff5

File tree

10 files changed

+437
-0
lines changed

10 files changed

+437
-0
lines changed

.gitignore

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/.settings/
2+
/.idea/
3+
target/
4+
*.log
5+
*.iml
6+
/\.target/
7+
/\.cache
8+
/\.project
9+
/\.classpath
10+
.history
11+
.DS_Store
12+
/tmp/
13+
/bin/
14+
/.cache-main
15+
/.cache-tests

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Scala 2.13 Collection Compatibility Library
2+
===========================================
3+
4+
This library for Scala 2.12 provides limited compatibility with the new collection library in 2.13. We try to keep the
5+
2.13 collections as backward compatible as possible but that is not always possible. For some of these cases this
6+
library allows you to compile sources written for a subset of the new semantics of 2.13 on 2.12.
7+
8+
For example, the `to` method is used with a type parameter in 2.12:
9+
10+
```scala
11+
xs.to[List]
12+
```
13+
14+
With this compatibility library you can also use the 2.13 syntax which uses a companion object:
15+
16+
```scala
17+
import scala.collection.compat._
18+
xs.to(List)
19+
```
20+
21+
This project can be cross-built on 2.13 (with new collections) and 2.12. The 2.13 version consists only of an
22+
empty `scala.collection.compat` package object that allows you to write `import scala.collection.compat._` in 2.13.
23+
The 2.13 version has the compatibility extensions in this package. It also adds backported version of some new collection
24+
types to other `scala.collection` subpackages.

build.sbt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
organization := "org.scala-lang"
2+
3+
name := "scala-collection-compat"
4+
5+
version := "0.1-SNAPSHOT"
6+
7+
resolvers += "scala-pr" at "https://scala-ci.typesafe.com/artifactory/scala-pr-validation-snapshots/"
8+
9+
unmanagedSourceDirectories in Compile ++=
10+
(if(scalaVersion.value.startsWith("2.13.")) Seq((sourceDirectory in Compile).value / "scala-2.13") else Seq())
11+
12+
crossScalaVersions := Seq("2.12.4", "2.13.0-pre-08218cd-SNAPSHOT")
13+
14+
scalaVersion := crossScalaVersions.value.head
15+
16+
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test"

project/build.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
sbt.version=0.13.16
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package scala.collection
2+
3+
import scala.collection.generic._
4+
import scala.collection.mutable.Builder
5+
import scala.reflect.ClassTag
6+
7+
/** The collection compatibility API */
8+
package object compat {
9+
import scala.collection.compat_impl._
10+
11+
implicit def genericCompanionToCBF[A, CC[X] <: GenTraversable[X]](fact: GenericCompanion[CC]): CanBuildFrom[Nothing, A, CC[A]] =
12+
simpleCBF(fact.newBuilder[A])
13+
14+
implicit def arrayCompanionToCBF[A : ClassTag](fact: Array.type): CanBuildFrom[Nothing, A, Array[A]] =
15+
simpleCBF(Array.newBuilder[A])
16+
17+
implicit def mapFactoryToCBF[K, V, CC[A, B] <: Map[A, B] with MapLike[A, B, CC[A, B]]](fact: MapFactory[CC]): CanBuildFrom[Nothing, (K, V), CC[K, V]] =
18+
simpleCBF(fact.newBuilder[K, V])
19+
20+
implicit def immutableBitSetFactoryToCBF(fact: BitSetFactory[immutable.BitSet]): CanBuildFrom[Nothing, Int, ImmutableBitSetCC[Int]] =
21+
simpleCBF(fact.newBuilder)
22+
23+
implicit def mutableBitSetFactoryToCBF(fact: BitSetFactory[mutable.BitSet]): CanBuildFrom[Nothing, Int, MutableBitSetCC[Int]] =
24+
simpleCBF(fact.newBuilder)
25+
26+
implicit class IterableFactoryExtensionMethods[CC[X] <: GenTraversable[X]](private val fact: GenericCompanion[CC]) {
27+
def from[A](source: TraversableOnce[A]): CC[A] = fact.apply(source.toSeq: _*)
28+
}
29+
30+
implicit class MapFactoryExtensionMethods[CC[A, B] <: Map[A, B] with MapLike[A, B, CC[A, B]]](private val fact: MapFactory[CC]) {
31+
def from[K, V](source: TraversableOnce[(K, V)]): CC[K, V] = fact.apply(source.toSeq: _*)
32+
}
33+
34+
implicit class BitSetFactoryExtensionMethods[C <: scala.collection.BitSet with scala.collection.BitSetLike[C]](private val fact: BitSetFactory[C]) {
35+
def fromSpecific(source: TraversableOnce[Int]): C = fact.apply(source.toSeq: _*)
36+
}
37+
38+
// This really belongs into scala.collection but there's already a package object in scala-library so we can't add to it
39+
type IterableOnce[+X] = TraversableOnce[X]
40+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package scala.collection
2+
3+
import scala.collection.generic._
4+
import scala.collection.mutable.Builder
5+
import scala.reflect.ClassTag
6+
7+
package object compat_impl {
8+
def simpleCBF[A, C](f: => Builder[A, C]): CanBuildFrom[Nothing, A, C] = new CanBuildFrom[Nothing, A, C] {
9+
def apply(from: Nothing): Builder[A, C] = apply()
10+
def apply(): Builder[A, C] = f
11+
}
12+
13+
type ImmutableBitSetCC[X] = ({ type L[_] = immutable.BitSet })#L[X]
14+
type MutableBitSetCC[X] = ({ type L[_] = mutable.BitSet })#L[X]
15+
}
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
package scala
2+
package collection
3+
package immutable
4+
5+
import scala.reflect.ClassTag
6+
import scala.runtime.BoxedUnit
7+
import scala.collection.generic._
8+
import scala.collection.mutable.{Builder, ArrayBuilder, ArrayBuffer, WrappedArrayBuilder}
9+
import scala.util.hashing.MurmurHash3
10+
import scala.annotation.unchecked.uncheckedVariance
11+
12+
import java.util.Arrays
13+
14+
/**
15+
* An immutable array.
16+
*
17+
* Supports efficient indexed access and has a small memory footprint.
18+
*
19+
* @define Coll `ImmutableArray`
20+
* @define coll wrapped array
21+
* @define orderDependent
22+
* @define orderDependentFold
23+
* @define mayNotTerminateInf
24+
* @define willNotTerminateInf
25+
*/
26+
abstract class ImmutableArray[+T]
27+
extends AbstractSeq[T]
28+
with IndexedSeq[T]
29+
{
30+
31+
override protected[this] def thisCollection: ImmutableArray[T] = this
32+
33+
/** The tag of the element type */
34+
protected[this] def elemTag: ClassTag[T]
35+
36+
/** The length of the array */
37+
def length: Int
38+
39+
/** The element at given index */
40+
def apply(index: Int): T
41+
42+
/** The underlying array */
43+
def unsafeArray: Array[T @uncheckedVariance]
44+
45+
private def elementClass: Class[_] =
46+
unsafeArray.getClass.getComponentType
47+
48+
override def stringPrefix = "ImmutableArray"
49+
50+
/** Clones this object, including the underlying Array. */
51+
override def clone(): ImmutableArray[T] = ImmutableArray unsafeWrapArray unsafeArray.clone()
52+
53+
/** Creates new builder for this collection ==> move to subclasses
54+
*/
55+
override protected[this] def newBuilder: Builder[T, ImmutableArray[T]] =
56+
new WrappedArrayBuilder[T](elemTag).mapResult(w => ImmutableArray.unsafeWrapArray(w.array))
57+
58+
}
59+
60+
/** A companion object used to create instances of `ImmutableArray`.
61+
*/
62+
object ImmutableArray {
63+
// This is reused for all calls to empty.
64+
private val EmptyImmutableArray = new ofRef[AnyRef](new Array[AnyRef](0))
65+
def empty[T <: AnyRef]: ImmutableArray[T] = EmptyImmutableArray.asInstanceOf[ImmutableArray[T]]
66+
67+
// If make is called explicitly we use whatever we're given, even if it's
68+
// empty. This may be unnecessary (if ImmutableArray is to honor the collections
69+
// contract all empty ones must be equal, so discriminating based on the reference
70+
// equality of an empty array should not come up) but we may as well be
71+
// conservative since wrapRefArray contributes most of the unnecessary allocations.
72+
def unsafeWrapArray[T](x: AnyRef): ImmutableArray[T] = (x match {
73+
case null => null
74+
case x: Array[AnyRef] => new ofRef[AnyRef](x)
75+
case x: Array[Int] => new ofInt(x)
76+
case x: Array[Double] => new ofDouble(x)
77+
case x: Array[Long] => new ofLong(x)
78+
case x: Array[Float] => new ofFloat(x)
79+
case x: Array[Char] => new ofChar(x)
80+
case x: Array[Byte] => new ofByte(x)
81+
case x: Array[Short] => new ofShort(x)
82+
case x: Array[Boolean] => new ofBoolean(x)
83+
case x: Array[Unit] => new ofUnit(x)
84+
}).asInstanceOf[ImmutableArray[T]]
85+
86+
implicit def canBuildFrom[T](implicit m: ClassTag[T]): CanBuildFrom[ImmutableArray[_], T, ImmutableArray[T]] =
87+
new CanBuildFrom[ImmutableArray[_], T, ImmutableArray[T]] {
88+
def apply(from: ImmutableArray[_]): Builder[T, ImmutableArray[T]] =
89+
ArrayBuilder.make[T]()(m) mapResult ImmutableArray.unsafeWrapArray[T]
90+
def apply: Builder[T, ImmutableArray[T]] =
91+
ArrayBuilder.make[T]()(m) mapResult ImmutableArray.unsafeWrapArray[T]
92+
}
93+
94+
final class ofRef[T <: AnyRef](val unsafeArray: Array[T]) extends ImmutableArray[T] with Serializable {
95+
lazy val elemTag = ClassTag[T](unsafeArray.getClass.getComponentType)
96+
def length: Int = unsafeArray.length
97+
def apply(index: Int): T = unsafeArray(index).asInstanceOf[T]
98+
def update(index: Int, elem: T) { unsafeArray(index) = elem }
99+
override def hashCode = MurmurHash3.wrappedArrayHash(unsafeArray)
100+
override def equals(that: Any) = that match {
101+
case that: ofRef[_] => Arrays.equals(unsafeArray.asInstanceOf[Array[AnyRef]], that.unsafeArray.asInstanceOf[Array[AnyRef]])
102+
case _ => super.equals(that)
103+
}
104+
}
105+
106+
final class ofByte(val unsafeArray: Array[Byte]) extends ImmutableArray[Byte] with Serializable {
107+
def elemTag = ClassTag.Byte
108+
def length: Int = unsafeArray.length
109+
def apply(index: Int): Byte = unsafeArray(index)
110+
def update(index: Int, elem: Byte) { unsafeArray(index) = elem }
111+
override def hashCode = MurmurHash3.wrappedBytesHash(unsafeArray)
112+
override def equals(that: Any) = that match {
113+
case that: ofByte => Arrays.equals(unsafeArray, that.unsafeArray)
114+
case _ => super.equals(that)
115+
}
116+
}
117+
118+
final class ofShort(val unsafeArray: Array[Short]) extends ImmutableArray[Short] with Serializable {
119+
def elemTag = ClassTag.Short
120+
def length: Int = unsafeArray.length
121+
def apply(index: Int): Short = unsafeArray(index)
122+
def update(index: Int, elem: Short) { unsafeArray(index) = elem }
123+
override def hashCode = MurmurHash3.wrappedArrayHash(unsafeArray)
124+
override def equals(that: Any) = that match {
125+
case that: ofShort => Arrays.equals(unsafeArray, that.unsafeArray)
126+
case _ => super.equals(that)
127+
}
128+
}
129+
130+
final class ofChar(val unsafeArray: Array[Char]) extends ImmutableArray[Char] with Serializable {
131+
def elemTag = ClassTag.Char
132+
def length: Int = unsafeArray.length
133+
def apply(index: Int): Char = unsafeArray(index)
134+
def update(index: Int, elem: Char) { unsafeArray(index) = elem }
135+
override def hashCode = MurmurHash3.wrappedArrayHash(unsafeArray)
136+
override def equals(that: Any) = that match {
137+
case that: ofChar => Arrays.equals(unsafeArray, that.unsafeArray)
138+
case _ => super.equals(that)
139+
}
140+
}
141+
142+
final class ofInt(val unsafeArray: Array[Int]) extends ImmutableArray[Int] with Serializable {
143+
def elemTag = ClassTag.Int
144+
def length: Int = unsafeArray.length
145+
def apply(index: Int): Int = unsafeArray(index)
146+
def update(index: Int, elem: Int) { unsafeArray(index) = elem }
147+
override def hashCode = MurmurHash3.wrappedArrayHash(unsafeArray)
148+
override def equals(that: Any) = that match {
149+
case that: ofInt => Arrays.equals(unsafeArray, that.unsafeArray)
150+
case _ => super.equals(that)
151+
}
152+
}
153+
154+
final class ofLong(val unsafeArray: Array[Long]) extends ImmutableArray[Long] with Serializable {
155+
def elemTag = ClassTag.Long
156+
def length: Int = unsafeArray.length
157+
def apply(index: Int): Long = unsafeArray(index)
158+
def update(index: Int, elem: Long) { unsafeArray(index) = elem }
159+
override def hashCode = MurmurHash3.wrappedArrayHash(unsafeArray)
160+
override def equals(that: Any) = that match {
161+
case that: ofLong => Arrays.equals(unsafeArray, that.unsafeArray)
162+
case _ => super.equals(that)
163+
}
164+
}
165+
166+
final class ofFloat(val unsafeArray: Array[Float]) extends ImmutableArray[Float] with Serializable {
167+
def elemTag = ClassTag.Float
168+
def length: Int = unsafeArray.length
169+
def apply(index: Int): Float = unsafeArray(index)
170+
def update(index: Int, elem: Float) { unsafeArray(index) = elem }
171+
override def hashCode = MurmurHash3.wrappedArrayHash(unsafeArray)
172+
override def equals(that: Any) = that match {
173+
case that: ofFloat => Arrays.equals(unsafeArray, that.unsafeArray)
174+
case _ => super.equals(that)
175+
}
176+
}
177+
178+
final class ofDouble(val unsafeArray: Array[Double]) extends ImmutableArray[Double] with Serializable {
179+
def elemTag = ClassTag.Double
180+
def length: Int = unsafeArray.length
181+
def apply(index: Int): Double = unsafeArray(index)
182+
def update(index: Int, elem: Double) { unsafeArray(index) = elem }
183+
override def hashCode = MurmurHash3.wrappedArrayHash(unsafeArray)
184+
override def equals(that: Any) = that match {
185+
case that: ofDouble => Arrays.equals(unsafeArray, that.unsafeArray)
186+
case _ => super.equals(that)
187+
}
188+
}
189+
190+
final class ofBoolean(val unsafeArray: Array[Boolean]) extends ImmutableArray[Boolean] with Serializable {
191+
def elemTag = ClassTag.Boolean
192+
def length: Int = unsafeArray.length
193+
def apply(index: Int): Boolean = unsafeArray(index)
194+
def update(index: Int, elem: Boolean) { unsafeArray(index) = elem }
195+
override def hashCode = MurmurHash3.wrappedArrayHash(unsafeArray)
196+
override def equals(that: Any) = that match {
197+
case that: ofBoolean => Arrays.equals(unsafeArray, that.unsafeArray)
198+
case _ => super.equals(that)
199+
}
200+
}
201+
202+
final class ofUnit(val unsafeArray: Array[Unit]) extends ImmutableArray[Unit] with Serializable {
203+
def elemTag = ClassTag.Unit
204+
def length: Int = unsafeArray.length
205+
def apply(index: Int): Unit = unsafeArray(index)
206+
def update(index: Int, elem: Unit) { unsafeArray(index) = elem }
207+
override def hashCode = MurmurHash3.wrappedArrayHash(unsafeArray)
208+
override def equals(that: Any) = that match {
209+
case that: ofUnit => unsafeArray.length == that.unsafeArray.length
210+
case _ => super.equals(that)
211+
}
212+
}
213+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package scala.collection
2+
3+
// Empty dummy package to allow cross-building with Scala 2.12
4+
package object compat {
5+
}

src/test/scala/CollectionTest.scala

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import java.util
2+
3+
import org.junit.Test
4+
import org.junit.Assert._
5+
6+
import scala.collection.immutable.BitSet
7+
import scala.collection.compat._
8+
9+
class CollectionTest {
10+
@Test
11+
def testTo: Unit = {
12+
val xs = List(1,2,3)
13+
val v = xs.to(Vector)
14+
val vT: Vector[Int] = v
15+
assertEquals(Vector(1,2,3), v)
16+
17+
val a = xs.to(Array)
18+
val aT: Array[Int] = a
19+
assertEquals(Vector(1,2,3), a.toVector)
20+
21+
val b = xs.to(BitSet) // we can fake a type constructor for the 2 standard BitSet types
22+
val bT: BitSet = b
23+
assertEquals(BitSet(1,2,3), b)
24+
25+
val ys = List(1 -> "a", 2 -> "b")
26+
val m = ys.to(Map)
27+
// Not possible - `to` returns a Col[A] so this is only typed as an Iterable[(Int, String)]
28+
//val mT: Map[Int, String] = m
29+
assertEquals(Map(1 -> "a", 2 -> "b"), m)
30+
assertTrue(m.isInstanceOf[Map[_, _]])
31+
}
32+
33+
@Test
34+
def testFrom: Unit = {
35+
val xs = List(1,2,3)
36+
val v = Vector.from(xs)
37+
val vT: Vector[Int] = v
38+
assertEquals(Vector(1,2,3), v)
39+
40+
val b = BitSet.fromSpecific(xs)
41+
val bT: BitSet = b
42+
assertEquals(BitSet(1,2,3), b)
43+
44+
val ys = List(1 -> "a", 2 -> "b")
45+
val m = Map.from(ys)
46+
val mT: Map[Int, String] = m
47+
assertEquals(Map(1 -> "a", 2 -> "b"), m)
48+
}
49+
}

0 commit comments

Comments
 (0)