From af2a0e66eb4b1204eac5dcb1d979486b92ef93d7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 9 Feb 2019 12:58:16 +0100 Subject: [PATCH] Add immutable array type IArray[T] is an opaque alias for Array[T] --- library/src-bootstrapped/scala/IArray.scala | 60 +++++++++++++++++++++ tests/run/iarrays.scala | 53 ++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 library/src-bootstrapped/scala/IArray.scala create mode 100644 tests/run/iarrays.scala diff --git a/library/src-bootstrapped/scala/IArray.scala b/library/src-bootstrapped/scala/IArray.scala new file mode 100644 index 000000000000..280c0557740d --- /dev/null +++ b/library/src-bootstrapped/scala/IArray.scala @@ -0,0 +1,60 @@ +package scala +import reflect.ClassTag + +/** It would be nice it IArray could be covariant, but unfortunately that's not + * possible. The problem is the preculiar behavior of `Array[Any]`. + * `Array[Any]` erases to `Object[]`. So, `IArray[Any]` also erases to `Object[]`. + * Bit this means `IArray[Int]`, which erases to `Int[]`, cannot be a subtype of + * `IArray[Any]`. + */ +opaque type IArray[T] = Array[T] + +object IArray { + + implied arrayOps { + inline def (arr: IArray[T]) apply[T] (n: Int): T = (arr: Array[T]).apply(n) + inline def (arr: IArray[T]) length[T] : Int = (arr: Array[T]).length + } + def apply[T: ClassTag](xs: T*): IArray[T] = Array(xs: _*) + + def apply(x: Boolean, xs: Boolean*): IArray[Boolean] = Array(x, xs: _*) + def apply(x: Byte, xs: Byte*): IArray[Byte] = Array(x, xs: _*) + def apply(x: Short, xs: Short*): IArray[Short] = Array(x, xs: _*) + def apply(x: Char, xs: Char*): IArray[Char] = Array(x, xs: _*) + def apply(x: Int, xs: Int*): IArray[Int] = Array(x, xs: _*) + def apply(x: Long, xs: Long*): IArray[Long] = Array(x, xs: _*) + def apply(x: Float, xs: Float*): IArray[Float] = Array(x, xs: _*) + def apply(x: Double, xs: Double*): IArray[Double] = Array(x, xs: _*) + def apply(x: Unit, xs: Unit*): IArray[Unit] = Array(x, xs: _*) + + def concat[T: ClassTag](xss: IArray[T]*): IArray[T] = Array.concat(xss: _*) + + def fill[T: ClassTag](n: Int)(elem: => T): IArray[T] = + Array.fill(n)(elem) + def fill[T: ClassTag](n1: Int, n2: Int)(elem: => T): IArray[IArray[T]] = + Array.fill(n1, n2)(elem) + def fill[T: ClassTag](n1: Int, n2: Int, n3: Int)(elem: => T): IArray[IArray[IArray[T]]] = + Array.fill(n1, n2, n3)(elem) + def fill[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int)(elem: => T): IArray[IArray[IArray[IArray[T]]]] = + Array.fill(n1, n2, n3, n4)(elem) + def fill[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(elem: => T): IArray[IArray[IArray[IArray[IArray[T]]]]] = + Array.fill(n1, n2, n3, n4, n5)(elem) + + def tabulate[T: ClassTag](n: Int)(f: Int => T): IArray[T] = + Array.tabulate(n)(f) + def tabulate[T: ClassTag](n1: Int, n2: Int)(f: (Int, Int) => T): IArray[IArray[T]] = + Array.tabulate(n1, n2)(f) + def tabulate[T: ClassTag](n1: Int, n2: Int, n3: Int)(f: (Int, Int, Int) => T): IArray[IArray[IArray[T]]] = + Array.tabulate(n1, n2, n3)(f) + def tabulate[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int)(f: (Int, Int, Int, Int) => T): IArray[IArray[IArray[IArray[T]]]] = + Array.tabulate(n1, n2, n3, n4)(f) + def tabulate[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(f: (Int, Int, Int, Int, Int) => T): IArray[IArray[IArray[IArray[IArray[T]]]]] = + Array.tabulate(n1, n2, n3, n4, n5)(f) + + def range(start: Int, end: Int): IArray[Int] = Array.range(start, end) + def range(start: Int, end: Int, step: Int): IArray[Int] = Array.range(start, end, step) + + def iterate[T: ClassTag](start: T, len: Int)(f: T => T): IArray[T] = Array.iterate(start, len)(f) + + def unapplySeq[T](x: IArray[T]): Option[IndexedSeq[T]] = Array.unapplySeq(x) +} \ No newline at end of file diff --git a/tests/run/iarrays.scala b/tests/run/iarrays.scala new file mode 100644 index 000000000000..a8d2b6dbfb9b --- /dev/null +++ b/tests/run/iarrays.scala @@ -0,0 +1,53 @@ +import reflect.ClassTag +object Test extends App { + + val xs = IArray(1, 2, 3) + + def f[T](ys: IArray[T]) = { + assert(ys.length == 3) + var sum = 0 + for (i <- 0 until ys.length) + sum += xs(i) + assert(sum == 6) + } + + f(xs) + + var sum = 0 + for (i <- 0 until xs.length) + sum += xs(i) + assert(sum == 6) + + def reduce[T](xs: IArray[T], z: T, op: (T, T) => T) = { + var acc = z + for (i <- 0 until xs.length) + acc = op(acc, xs(i)) + acc + } + + def flatten[T: ClassTag](ys: IArray[IArray[T]]) = { + var len = 0 + for (i <- 0 until ys.length) len += ys(i).length + val flat = new Array[T](len) + var k = 0 + for (i <- 0 until ys.length) { + for (j <- 0 until ys(i).length) { + flat(k) = ys(i)(j) + k += 1 + } + } + IArray(flat: _*) + } + + val ys = IArray.concat(xs, xs, xs) + assert(reduce(ys, 0, _ + _) == 18) + + val zss = IArray.fill(2, 3)(1) + val zs = flatten(zss) + assert(reduce(zs, 0, _ + _) == 6) + + val is = IArray.iterate(0, 4)(_ + 1) + assert(reduce(is, 0, _ + _) == 6) + + val IArray(1, 2, 3) = xs +} \ No newline at end of file