@@ -7,6 +7,9 @@ import reflect.ClassTag
77object opaques :
88 opaque type IArray [+ T ] = Array [_ <: T ]
99
10+ private [scala] type Sub [A ] >: Array [A ] <: IArray [A ]
11+ private [scala] type Sup [A ] >: IArray [A ] <: Array [_ <: A ]
12+
1013 /** Defines extension methods for immutable arrays */
1114 given arrayOps : Object {
1215
@@ -41,39 +44,41 @@ object opaques:
4144
4245 /** Returns this array concatenated with the given array. */
4346 def [T , U >: T : ClassTag ](arr : IArray [T ]) ++ (that : IArray [U ]): IArray [U ] =
44- ( genericArrayOps(arr) ++ that. asInstanceOf [ Array [ U ]]). asInstanceOf [ IArray [ U ]]
47+ genericArrayOps(arr) ++ that
4548
4649 /** Tests whether this array contains a given value as an element. */
4750 def [T ](arr : IArray [T ]) contains(elem : T ): Boolean =
48- genericArrayOps(arr.asInstanceOf [Array [T ]]).contains(elem)
51+ // `genericArrayOps(arr).contains(elem)` does not work because `elem` does not have type `arr.T`
52+ // but we can use `exists` instead, which is how `ArrayOps#contains` itself is implemented:
53+ genericArrayOps(arr).exists(_ == elem)
4954
5055 /** Counts the number of elements in this array which satisfy a predicate */
5156 def [T ](arr : IArray [T ]) count(p : T => Boolean ): Int =
5257 genericArrayOps(arr).count(p)
5358
5459 /** The rest of the array without its `n` first elements. */
5560 def [T ](arr : IArray [T ]) drop(n : Int ): IArray [T ] =
56- genericArrayOps(arr).drop(n). asInstanceOf [ IArray [ T ]]
61+ genericArrayOps(arr).drop(n)
5762
5863 /** The rest of the array without its `n` last elements. */
5964 def [T ](arr : IArray [T ]) dropRight(n : Int ): IArray [T ] =
60- genericArrayOps(arr).dropRight(n). asInstanceOf [ IArray [ T ]]
65+ genericArrayOps(arr).dropRight(n)
6166
6267 /** Drops longest prefix of elements that satisfy a predicate. */
6368 def [T ](arr : IArray [T ]) dropWhile(p : T => Boolean ): IArray [T ] =
64- genericArrayOps(arr).dropWhile(p). asInstanceOf [ IArray [ T ]]
69+ genericArrayOps(arr).dropWhile(p)
6570
6671 /** Tests whether a predicate holds for at least one element of this array. */
67- def [T ](arr : IArray [T ]) exists(p : T => Boolean ): IArray [ T ] =
68- genericArrayOps(arr).exists(p). asInstanceOf [ IArray [ T ]]
72+ def [T ](arr : IArray [T ]) exists(p : T => Boolean ): Boolean =
73+ genericArrayOps(arr).exists(p)
6974
7075 /** Selects all elements of this array which satisfy a predicate. */
7176 def [T ](arr : IArray [T ]) filter(p : T => Boolean ): IArray [T ] =
72- genericArrayOps(arr).filter(p). asInstanceOf [ IArray [ T ]]
77+ genericArrayOps(arr).filter(p)
7378
7479 /** Selects all elements of this array which do not satisfy a predicate. */
7580 def [T ](arr : IArray [T ]) filterNot(p : T => Boolean ): IArray [T ] =
76- genericArrayOps(arr).filterNot(p). asInstanceOf [ IArray [ T ]]
81+ genericArrayOps(arr).filterNot(p)
7782
7883 /** Finds the first element of the array satisfying a predicate, if any. */
7984 def [T ](arr : IArray [T ]) find(p : T => Boolean ): Option [T ] =
@@ -82,12 +87,12 @@ object opaques:
8287 /** Builds a new array by applying a function to all elements of this array
8388 * and using the elements of the resulting collections. */
8489 def [T , U : ClassTag ](arr : IArray [T ]) flatMap(f : T => IterableOnce [U ]): IArray [U ] =
85- genericArrayOps(arr).flatMap(f). asInstanceOf [ IArray [ U ]]
90+ genericArrayOps(arr).flatMap(f)
8691
8792 /** Flattens a two-dimensional array by concatenating all its rows
8893 * into a single array. */
8994 def [T , U : ClassTag ](arr : IArray [T ]) flatten(using T => Iterable [U ]): IArray [U ] =
90- genericArrayOps(arr).flatten. asInstanceOf [ IArray [ U ]]
95+ genericArrayOps(arr).flatten
9196
9297 /** Folds the elements of this array using the specified associative binary operator. */
9398 def [T , U >: T : ClassTag ](arr : IArray [T ]) fold(z : U )(op : (U , U ) => U ): U =
@@ -121,7 +126,10 @@ object opaques:
121126
122127 /** Finds index of first occurrence of some value in this array after or at some start index. */
123128 def [T ](arr : IArray [T ]) indexOf(elem : T , from : Int = 0 ): Int =
124- genericArrayOps(arr.asInstanceOf [Array [T ]]).indexOf(elem, from)
129+ // `asInstanceOf` needed because `elem` does not have type `arr.T`
130+ // We could use `arr.iterator.indexOf(elem, from)` or `arr.indexWhere(_ == elem, from)`
131+ // but these would incur some overhead.
132+ genericArrayOps(arr).indexOf(elem.asInstanceOf , from)
125133
126134 /** Finds index of the first element satisfying some predicate after or at some start index. */
127135 def [T ](arr : IArray [T ]) indexWhere(p : T => Boolean , from : Int = 0 ): Int =
@@ -133,7 +141,7 @@ object opaques:
133141
134142 /** The initial part of the array without its last element. */
135143 def [T ](arr : IArray [T ]) init : IArray [T ] =
136- genericArrayOps(arr).init. asInstanceOf [ IArray [ T ]]
144+ genericArrayOps(arr).init
137145
138146 /** Tests whether the array is empty. */
139147 def [T ](arr : IArray [T ]) isEmpty : Boolean =
@@ -153,136 +161,134 @@ object opaques:
153161
154162 /** Finds index of last occurrence of some value in this array before or at a given end index. */
155163 def [T ](arr : IArray [T ]) lastIndexOf(elem : T , end : Int = arr.length - 1 ): Int =
156- genericArrayOps(arr.asInstanceOf [Array [T ]]).lastIndexOf(elem, end)
164+ // see: same issue in `indexOf`
165+ genericArrayOps(arr).lastIndexOf(elem.asInstanceOf , end)
157166
158167 /** Finds index of last element satisfying some predicate before or at given end index. */
159168 def [T ](arr : IArray [T ]) lastIndexWhere(p : T => Boolean , end : Int = arr.length - 1 ): Int =
160169 genericArrayOps(arr).lastIndexWhere(p, end)
161170
162171 /** Builds a new array by applying a function to all elements of this array. */
163172 def [T , U : ClassTag ](arr : IArray [T ]) map(f : T => U ): IArray [U ] =
164- genericArrayOps(arr).map(f). asInstanceOf [ IArray [ U ]]
173+ genericArrayOps(arr).map(f)
165174
166175 /** Tests whether the array is not empty. */
167176 def [T ](arr : IArray [T ]) nonEmpty : Boolean =
168177 genericArrayOps(arr).nonEmpty
169178
170179 /** A pair of, first, all elements that satisfy predicate `p` and, second, all elements that do not. */
171180 def [T ](arr : IArray [T ]) partition(p : T => Boolean ): (IArray [T ], IArray [T ]) =
172- genericArrayOps(arr).partition(p) match {
173- case (x, y) => (x.asInstanceOf [IArray [T ]], y.asInstanceOf [IArray [T ]])
174- }
181+ genericArrayOps(arr).partition(p)
175182
176183 /** Returns a new array with the elements in reversed order. */
177184 def [T ](arr : IArray [T ]) reverse : IArray [T ] =
178- genericArrayOps(arr).reverse. asInstanceOf [ IArray [ T ]]
185+ genericArrayOps(arr).reverse
179186
180187 /** Computes a prefix scan of the elements of the array. */
181188 def [T , U >: T : ClassTag ](arr : IArray [T ]) scan(z : U )(op : (U , U ) => U ): IArray [U ] =
182- genericArrayOps(arr).scan(z)(op). asInstanceOf [ IArray [ U ]]
189+ genericArrayOps(arr).scan(z)(op)
183190
184191 /** Produces an array containing cumulative results of applying the binary
185192 * operator going left to right. */
186193 def [T , U : ClassTag ](arr : IArray [T ]) scanLeft(z : U )(op : (U , T ) => U ): IArray [U ] =
187- genericArrayOps(arr).scanLeft(z)(op). asInstanceOf [ IArray [ U ]]
194+ genericArrayOps(arr).scanLeft(z)(op)
188195
189196 /** Produces an array containing cumulative results of applying the binary
190197 * operator going right to left. */
191198 def [T , U : ClassTag ](arr : IArray [T ]) scanRight(z : U )(op : (T , U ) => U ): IArray [U ] =
192- genericArrayOps(arr).scanRight(z)(op). asInstanceOf [ IArray [ U ]]
199+ genericArrayOps(arr).scanRight(z)(op)
193200
194201 /** The size of this array. */
195202 def [T ](arr : IArray [T ]) size : Int =
196203 arr.length
197204
198205 /** Selects the interval of elements between the given indices. */
199206 def [T ](arr : IArray [T ]) slice(from : Int , until : Int ): IArray [T ] =
200- genericArrayOps(arr).slice(from, until). asInstanceOf [ IArray [ T ]]
207+ genericArrayOps(arr).slice(from, until)
201208
202209 /** Sorts this array according to the Ordering which results from transforming
203210 * an implicitly given Ordering with a transformation function. */
204211 def [T , U : ClassTag ](arr : IArray [T ]) sortBy(f : T => U )(using math.Ordering [U ]): IArray [T ] =
205- genericArrayOps(arr).sortBy(f). asInstanceOf [ IArray [ T ]]
212+ genericArrayOps(arr).sortBy(f)
206213
207214 /** Sorts this array according to a comparison function. */
208215 def [T ](arr : IArray [T ]) sortWith(f : (T , T ) => Boolean ): IArray [T ] =
209- genericArrayOps(arr).sortWith(f). asInstanceOf [ IArray [ T ]]
216+ genericArrayOps(arr).sortWith(f)
210217
211218 /** Sorts this array according to an Ordering. */
212219 def [T ](arr : IArray [T ]) sorted(using math.Ordering [T ]): IArray [T ] =
213- genericArrayOps(arr).sorted. asInstanceOf [ IArray [ T ]]
220+ genericArrayOps(arr).sorted
214221
215222 /** Splits this array into a prefix/suffix pair according to a predicate. */
216223 def [T ](arr : IArray [T ]) span(p : T => Boolean ): (IArray [T ], IArray [T ]) =
217- genericArrayOps(arr).span(p) match {
218- case (x, y) => (x.asInstanceOf [IArray [T ]], y.asInstanceOf [IArray [T ]])
219- }
224+ genericArrayOps(arr).span(p)
220225
221226 /** Splits this array into two at a given position. */
222227 def [T ](arr : IArray [T ]) splitAt(n : Int ): (IArray [T ], IArray [T ]) =
223- genericArrayOps(arr).splitAt(n) match {
224- case (x, y) => (x.asInstanceOf [IArray [T ]], y.asInstanceOf [IArray [T ]])
225- }
228+ genericArrayOps(arr).splitAt(n)
226229
227230 /** Tests whether this array starts with the given array. */
228231 def [T , U >: T : ClassTag ](arr : IArray [T ]) startsWith(that : IArray [U ], offset : Int = 0 ): Boolean =
229- genericArrayOps(arr).startsWith(that. asInstanceOf [ Array [ U ]] )
232+ genericArrayOps(arr).startsWith(that)
230233
231234 /** The rest of the array without its first element. */
232235 def [T ](arr : IArray [T ]) tail : IArray [T ] =
233- genericArrayOps(arr).tail. asInstanceOf [ IArray [ T ]]
236+ genericArrayOps(arr).tail
234237
235238 /** An array containing the first `n` elements of this array. */
236239 def [T ](arr : IArray [T ]) take(n : Int ): IArray [T ] =
237- genericArrayOps(arr).take(n). asInstanceOf [ IArray [ T ]]
240+ genericArrayOps(arr).take(n)
238241
239242 /** An array containing the last `n` elements of this array. */
240243 def [T ](arr : IArray [T ]) takeRight(n : Int ): IArray [T ] =
241- genericArrayOps(arr).takeRight(n). asInstanceOf [ IArray [ T ]]
244+ genericArrayOps(arr).takeRight(n)
242245
243246 /** Takes longest prefix of elements that satisfy a predicate. */
244247 def [T ](arr : IArray [T ]) takeWhile(p : T => Boolean ): IArray [T ] =
245- genericArrayOps(arr).takeWhile(p). asInstanceOf [ IArray [ T ]]
248+ genericArrayOps(arr).takeWhile(p)
246249
247250 /** Converts an array of pairs into an array of first elements and an array of second elements. */
248251 def [U : ClassTag , V : ClassTag ](arr : IArray [(U , V )]) unzip : (IArray [U ], IArray [V ]) =
249- genericArrayOps(arr).unzip match {
250- case (x, y) => (x.asInstanceOf [IArray [U ]], y.asInstanceOf [IArray [V ]])
251- }
252+ genericArrayOps(arr).unzip
252253
253254 /** Returns an array formed from this array and another iterable collection
254255 * by combining corresponding elements in pairs.
255256 * If one of the two collections is longer than the other, its remaining elements are ignored. */
256257 def [T , U : ClassTag ](arr : IArray [T ]) zip(that : IArray [U ]): IArray [(T , U )] =
257- genericArrayOps(arr).zip(that). asInstanceOf [ IArray [( T , U )]]
258+ genericArrayOps(arr).zip(that)
258259 }
259260end opaques
260261
261262type IArray [+ T ] = opaques.IArray [T ]
262263
263264object IArray {
265+ import opaques .Sub
266+ import opaques .Sup
267+
268+ // A convenience to avoid having to cast everything by hand
269+ private given [A ] as Conversion [Array [A ], IArray [A ]] = identity[Sub [A ]]
264270
265271 /** An immutable array of length 0. */
266- def empty [T : ClassTag ]: IArray [T ] = new Array [T ](0 ). asInstanceOf
272+ def empty [T : ClassTag ]: IArray [T ] = new Array [T ](0 )
267273
268274 /** An immutable boolean array of length 0. */
269- def emptyBooleanIArray = Array .emptyBooleanArray. asInstanceOf [ IArray [ Boolean ]]
275+ def emptyBooleanIArray : IArray [ Boolean ] = Array .emptyBooleanArray
270276 /** An immutable byte array of length 0. */
271- def emptyByteIArray = Array .emptyByteArray. asInstanceOf [ IArray [ Byte ]]
277+ def emptyByteIArray : IArray [ Byte ] = Array .emptyByteArray
272278 /** An immutable char array of length 0. */
273- def emptyCharIArray = Array .emptyCharArray. asInstanceOf [ IArray [ Char ]]
279+ def emptyCharIArray : IArray [ Char ] = Array .emptyCharArray
274280 /** An immutable double array of length 0. */
275- def emptyDoubleIArray = Array .emptyDoubleArray. asInstanceOf [ IArray [ Double ]]
281+ def emptyDoubleIArray : IArray [ Double ] = Array .emptyDoubleArray
276282 /** An immutable float array of length 0. */
277- def emptyFloatIArray = Array .emptyFloatArray. asInstanceOf [ IArray [ Float ]]
283+ def emptyFloatIArray : IArray [ Float ] = Array .emptyFloatArray
278284 /** An immutable int array of length 0. */
279- def emptyIntIArray = Array .emptyIntArray. asInstanceOf [ IArray [ Int ]]
285+ def emptyIntIArray : IArray [ Int ] = Array .emptyIntArray
280286 /** An immutable long array of length 0. */
281- def emptyLongIArray = Array .emptyLongArray. asInstanceOf [ IArray [ Long ]]
287+ def emptyLongIArray : IArray [ Long ] = Array .emptyLongArray
282288 /** An immutable short array of length 0. */
283- def emptyShortIArray = Array .emptyShortArray. asInstanceOf [ IArray [ Short ]]
289+ def emptyShortIArray : IArray [ Short ] = Array .emptyShortArray
284290 /** An immutable object array of length 0. */
285- def emptyObjectIArray = Array .emptyObjectArray. asInstanceOf [ IArray [ Object ]]
291+ def emptyObjectIArray : IArray [ Object ] = Array .emptyObjectArray
286292
287293 /** An immutable array with given elements. */
288294 inline def apply [T ](inline xs : T * )(using inline ct : ClassTag [T ]): IArray [T ] = Array (xs : _* ).asInstanceOf
@@ -311,7 +317,10 @@ object IArray {
311317 * @return the array created from concatenating `xss`
312318 */
313319 def concat [T : ClassTag ](xss : IArray [T ]* ): IArray [T ] =
314- Array .concat[T ](xss.asInstanceOf [Seq [Array [T ]]]: _* ).asInstanceOf
320+ // `Array.concat` should arguably take in a `Seq[Array[_ <: T]]`,
321+ // but since it currently takes a `Seq[Array[T]]` we have to perform a cast,
322+ // knowing tacitly that `concat` is not going to do the wrong thing.
323+ Array .concat[T ](xss.asInstanceOf [Seq [Array [T ]]]: _* )
315324
316325 /** Returns an immutable array that contains the results of some element computation a number
317326 * of times. Each element is determined by a separate computation.
@@ -320,7 +329,7 @@ object IArray {
320329 * @param elem the element computation
321330 */
322331 def fill [T : ClassTag ](n : Int )(elem : => T ): IArray [T ] =
323- Array .fill(n)(elem). asInstanceOf
332+ Array .fill(n)(elem)
324333
325334 /** Returns a two-dimensional immutable array that contains the results of some element computation a number
326335 * of times. Each element is determined by a separate computation.
@@ -330,6 +339,7 @@ object IArray {
330339 * @param elem the element computation
331340 */
332341 def fill [T : ClassTag ](n1 : Int , n2 : Int )(elem : => T ): IArray [IArray [T ]] =
342+ // We cannot avoid a cast here as Array.fill creates inner arrays out of our control:
333343 Array .fill(n1, n2)(elem).asInstanceOf
334344
335345 /** Returns a three-dimensional immutable array that contains the results of some element computation a number
@@ -375,7 +385,7 @@ object IArray {
375385 * @param f The function computing element values
376386 */
377387 def tabulate [T : ClassTag ](n : Int )(f : Int => T ): IArray [T ] =
378- Array .tabulate(n)(f). asInstanceOf
388+ Array .tabulate(n)(f)
379389
380390 /** Returns a two-dimensional immutable array containing values of a given function
381391 * over ranges of integer values starting from `0`.
@@ -430,7 +440,7 @@ object IArray {
430440 * @return the immutable array with values in range `start, start + 1, ..., end - 1`
431441 * up to, but excluding, `end`.
432442 */
433- def range (start : Int , end : Int ): IArray [Int ] = Array .range(start, end). asInstanceOf
443+ def range (start : Int , end : Int ): IArray [Int ] = Array .range(start, end)
434444
435445 /** Returns an immutable array containing equally spaced values in some integer interval.
436446 *
@@ -439,7 +449,7 @@ object IArray {
439449 * @param step the increment value of the array (may not be zero)
440450 * @return the immutable array with values in `start, start + step, ...` up to, but excluding `end`
441451 */
442- def range (start : Int , end : Int , step : Int ): IArray [Int ] = Array .range(start, end, step). asInstanceOf
452+ def range (start : Int , end : Int , step : Int ): IArray [Int ] = Array .range(start, end, step)
443453
444454 /** Returns an immutable array containing repeated applications of a function to a start value.
445455 *
@@ -448,13 +458,16 @@ object IArray {
448458 * @param f the function that is repeatedly applied
449459 * @return the immutable array returning `len` values in the sequence `start, f(start), f(f(start)), ...`
450460 */
451- def iterate [T : ClassTag ](start : T , len : Int )(f : T => T ): IArray [T ] = Array .iterate(start, len)(f). asInstanceOf
461+ def iterate [T : ClassTag ](start : T , len : Int )(f : T => T ): IArray [T ] = Array .iterate(start, len)(f)
452462
453463 /** Returns a decomposition of the array into a sequence. This supports
454464 * a pattern match like `{ case IArray(x,y,z) => println('3 elements')}`.
455465 *
456466 * @param x the selector value
457467 * @return sequence wrapped in a [[scala.Some ]], if `x` is a Seq, otherwise `None`
458468 */
459- def unapplySeq [T ](x : IArray [T ]) = Array .unapplySeq[T ](x.asInstanceOf [Array [T ]])
460- }
469+ def unapplySeq [T ](x : IArray [T ]) =
470+ // The double type ascription is currently needed,
471+ // for some reason (see: https://scastie.scala-lang.org/sSsmOhKxSKym405MgNRKqQ)
472+ Array .unapplySeq((x : Sup [T ]): Array [_ <: T ])
473+ }
0 commit comments