@@ -7,6 +7,9 @@ import reflect.ClassTag
7
7
object opaques :
8
8
opaque type IArray [+ T ] = Array [_ <: T ]
9
9
10
+ private [scala] type Sub [A ] >: Array [A ] <: IArray [A ]
11
+ private [scala] type Sup [A ] >: IArray [A ] <: Array [_ <: A ]
12
+
10
13
/** Defines extension methods for immutable arrays */
11
14
given arrayOps : Object {
12
15
@@ -41,39 +44,41 @@ object opaques:
41
44
42
45
/** Returns this array concatenated with the given array. */
43
46
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
45
48
46
49
/** Tests whether this array contains a given value as an element. */
47
50
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)
49
54
50
55
/** Counts the number of elements in this array which satisfy a predicate */
51
56
def [T ](arr : IArray [T ]) count(p : T => Boolean ): Int =
52
57
genericArrayOps(arr).count(p)
53
58
54
59
/** The rest of the array without its `n` first elements. */
55
60
def [T ](arr : IArray [T ]) drop(n : Int ): IArray [T ] =
56
- genericArrayOps(arr).drop(n). asInstanceOf [ IArray [ T ]]
61
+ genericArrayOps(arr).drop(n)
57
62
58
63
/** The rest of the array without its `n` last elements. */
59
64
def [T ](arr : IArray [T ]) dropRight(n : Int ): IArray [T ] =
60
- genericArrayOps(arr).dropRight(n). asInstanceOf [ IArray [ T ]]
65
+ genericArrayOps(arr).dropRight(n)
61
66
62
67
/** Drops longest prefix of elements that satisfy a predicate. */
63
68
def [T ](arr : IArray [T ]) dropWhile(p : T => Boolean ): IArray [T ] =
64
- genericArrayOps(arr).dropWhile(p). asInstanceOf [ IArray [ T ]]
69
+ genericArrayOps(arr).dropWhile(p)
65
70
66
71
/** 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)
69
74
70
75
/** Selects all elements of this array which satisfy a predicate. */
71
76
def [T ](arr : IArray [T ]) filter(p : T => Boolean ): IArray [T ] =
72
- genericArrayOps(arr).filter(p). asInstanceOf [ IArray [ T ]]
77
+ genericArrayOps(arr).filter(p)
73
78
74
79
/** Selects all elements of this array which do not satisfy a predicate. */
75
80
def [T ](arr : IArray [T ]) filterNot(p : T => Boolean ): IArray [T ] =
76
- genericArrayOps(arr).filterNot(p). asInstanceOf [ IArray [ T ]]
81
+ genericArrayOps(arr).filterNot(p)
77
82
78
83
/** Finds the first element of the array satisfying a predicate, if any. */
79
84
def [T ](arr : IArray [T ]) find(p : T => Boolean ): Option [T ] =
@@ -82,12 +87,12 @@ object opaques:
82
87
/** Builds a new array by applying a function to all elements of this array
83
88
* and using the elements of the resulting collections. */
84
89
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)
86
91
87
92
/** Flattens a two-dimensional array by concatenating all its rows
88
93
* into a single array. */
89
94
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
91
96
92
97
/** Folds the elements of this array using the specified associative binary operator. */
93
98
def [T , U >: T : ClassTag ](arr : IArray [T ]) fold(z : U )(op : (U , U ) => U ): U =
@@ -121,7 +126,10 @@ object opaques:
121
126
122
127
/** Finds index of first occurrence of some value in this array after or at some start index. */
123
128
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)
125
133
126
134
/** Finds index of the first element satisfying some predicate after or at some start index. */
127
135
def [T ](arr : IArray [T ]) indexWhere(p : T => Boolean , from : Int = 0 ): Int =
@@ -133,7 +141,7 @@ object opaques:
133
141
134
142
/** The initial part of the array without its last element. */
135
143
def [T ](arr : IArray [T ]) init : IArray [T ] =
136
- genericArrayOps(arr).init. asInstanceOf [ IArray [ T ]]
144
+ genericArrayOps(arr).init
137
145
138
146
/** Tests whether the array is empty. */
139
147
def [T ](arr : IArray [T ]) isEmpty : Boolean =
@@ -153,136 +161,134 @@ object opaques:
153
161
154
162
/** Finds index of last occurrence of some value in this array before or at a given end index. */
155
163
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)
157
166
158
167
/** Finds index of last element satisfying some predicate before or at given end index. */
159
168
def [T ](arr : IArray [T ]) lastIndexWhere(p : T => Boolean , end : Int = arr.length - 1 ): Int =
160
169
genericArrayOps(arr).lastIndexWhere(p, end)
161
170
162
171
/** Builds a new array by applying a function to all elements of this array. */
163
172
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)
165
174
166
175
/** Tests whether the array is not empty. */
167
176
def [T ](arr : IArray [T ]) nonEmpty : Boolean =
168
177
genericArrayOps(arr).nonEmpty
169
178
170
179
/** A pair of, first, all elements that satisfy predicate `p` and, second, all elements that do not. */
171
180
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)
175
182
176
183
/** Returns a new array with the elements in reversed order. */
177
184
def [T ](arr : IArray [T ]) reverse : IArray [T ] =
178
- genericArrayOps(arr).reverse. asInstanceOf [ IArray [ T ]]
185
+ genericArrayOps(arr).reverse
179
186
180
187
/** Computes a prefix scan of the elements of the array. */
181
188
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)
183
190
184
191
/** Produces an array containing cumulative results of applying the binary
185
192
* operator going left to right. */
186
193
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)
188
195
189
196
/** Produces an array containing cumulative results of applying the binary
190
197
* operator going right to left. */
191
198
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)
193
200
194
201
/** The size of this array. */
195
202
def [T ](arr : IArray [T ]) size : Int =
196
203
arr.length
197
204
198
205
/** Selects the interval of elements between the given indices. */
199
206
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)
201
208
202
209
/** Sorts this array according to the Ordering which results from transforming
203
210
* an implicitly given Ordering with a transformation function. */
204
211
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)
206
213
207
214
/** Sorts this array according to a comparison function. */
208
215
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)
210
217
211
218
/** Sorts this array according to an Ordering. */
212
219
def [T ](arr : IArray [T ]) sorted(using math.Ordering [T ]): IArray [T ] =
213
- genericArrayOps(arr).sorted. asInstanceOf [ IArray [ T ]]
220
+ genericArrayOps(arr).sorted
214
221
215
222
/** Splits this array into a prefix/suffix pair according to a predicate. */
216
223
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)
220
225
221
226
/** Splits this array into two at a given position. */
222
227
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)
226
229
227
230
/** Tests whether this array starts with the given array. */
228
231
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)
230
233
231
234
/** The rest of the array without its first element. */
232
235
def [T ](arr : IArray [T ]) tail : IArray [T ] =
233
- genericArrayOps(arr).tail. asInstanceOf [ IArray [ T ]]
236
+ genericArrayOps(arr).tail
234
237
235
238
/** An array containing the first `n` elements of this array. */
236
239
def [T ](arr : IArray [T ]) take(n : Int ): IArray [T ] =
237
- genericArrayOps(arr).take(n). asInstanceOf [ IArray [ T ]]
240
+ genericArrayOps(arr).take(n)
238
241
239
242
/** An array containing the last `n` elements of this array. */
240
243
def [T ](arr : IArray [T ]) takeRight(n : Int ): IArray [T ] =
241
- genericArrayOps(arr).takeRight(n). asInstanceOf [ IArray [ T ]]
244
+ genericArrayOps(arr).takeRight(n)
242
245
243
246
/** Takes longest prefix of elements that satisfy a predicate. */
244
247
def [T ](arr : IArray [T ]) takeWhile(p : T => Boolean ): IArray [T ] =
245
- genericArrayOps(arr).takeWhile(p). asInstanceOf [ IArray [ T ]]
248
+ genericArrayOps(arr).takeWhile(p)
246
249
247
250
/** Converts an array of pairs into an array of first elements and an array of second elements. */
248
251
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
252
253
253
254
/** Returns an array formed from this array and another iterable collection
254
255
* by combining corresponding elements in pairs.
255
256
* If one of the two collections is longer than the other, its remaining elements are ignored. */
256
257
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)
258
259
}
259
260
end opaques
260
261
261
262
type IArray [+ T ] = opaques.IArray [T ]
262
263
263
264
object 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 ]]
264
270
265
271
/** 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 )
267
273
268
274
/** An immutable boolean array of length 0. */
269
- def emptyBooleanIArray = Array .emptyBooleanArray. asInstanceOf [ IArray [ Boolean ]]
275
+ def emptyBooleanIArray : IArray [ Boolean ] = Array .emptyBooleanArray
270
276
/** An immutable byte array of length 0. */
271
- def emptyByteIArray = Array .emptyByteArray. asInstanceOf [ IArray [ Byte ]]
277
+ def emptyByteIArray : IArray [ Byte ] = Array .emptyByteArray
272
278
/** An immutable char array of length 0. */
273
- def emptyCharIArray = Array .emptyCharArray. asInstanceOf [ IArray [ Char ]]
279
+ def emptyCharIArray : IArray [ Char ] = Array .emptyCharArray
274
280
/** An immutable double array of length 0. */
275
- def emptyDoubleIArray = Array .emptyDoubleArray. asInstanceOf [ IArray [ Double ]]
281
+ def emptyDoubleIArray : IArray [ Double ] = Array .emptyDoubleArray
276
282
/** An immutable float array of length 0. */
277
- def emptyFloatIArray = Array .emptyFloatArray. asInstanceOf [ IArray [ Float ]]
283
+ def emptyFloatIArray : IArray [ Float ] = Array .emptyFloatArray
278
284
/** An immutable int array of length 0. */
279
- def emptyIntIArray = Array .emptyIntArray. asInstanceOf [ IArray [ Int ]]
285
+ def emptyIntIArray : IArray [ Int ] = Array .emptyIntArray
280
286
/** An immutable long array of length 0. */
281
- def emptyLongIArray = Array .emptyLongArray. asInstanceOf [ IArray [ Long ]]
287
+ def emptyLongIArray : IArray [ Long ] = Array .emptyLongArray
282
288
/** An immutable short array of length 0. */
283
- def emptyShortIArray = Array .emptyShortArray. asInstanceOf [ IArray [ Short ]]
289
+ def emptyShortIArray : IArray [ Short ] = Array .emptyShortArray
284
290
/** An immutable object array of length 0. */
285
- def emptyObjectIArray = Array .emptyObjectArray. asInstanceOf [ IArray [ Object ]]
291
+ def emptyObjectIArray : IArray [ Object ] = Array .emptyObjectArray
286
292
287
293
/** An immutable array with given elements. */
288
294
inline def apply [T ](inline xs : T * )(using inline ct : ClassTag [T ]): IArray [T ] = Array (xs : _* ).asInstanceOf
@@ -311,7 +317,10 @@ object IArray {
311
317
* @return the array created from concatenating `xss`
312
318
*/
313
319
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 ]]]: _* )
315
324
316
325
/** Returns an immutable array that contains the results of some element computation a number
317
326
* of times. Each element is determined by a separate computation.
@@ -320,7 +329,7 @@ object IArray {
320
329
* @param elem the element computation
321
330
*/
322
331
def fill [T : ClassTag ](n : Int )(elem : => T ): IArray [T ] =
323
- Array .fill(n)(elem). asInstanceOf
332
+ Array .fill(n)(elem)
324
333
325
334
/** Returns a two-dimensional immutable array that contains the results of some element computation a number
326
335
* of times. Each element is determined by a separate computation.
@@ -330,6 +339,7 @@ object IArray {
330
339
* @param elem the element computation
331
340
*/
332
341
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:
333
343
Array .fill(n1, n2)(elem).asInstanceOf
334
344
335
345
/** Returns a three-dimensional immutable array that contains the results of some element computation a number
@@ -375,7 +385,7 @@ object IArray {
375
385
* @param f The function computing element values
376
386
*/
377
387
def tabulate [T : ClassTag ](n : Int )(f : Int => T ): IArray [T ] =
378
- Array .tabulate(n)(f). asInstanceOf
388
+ Array .tabulate(n)(f)
379
389
380
390
/** Returns a two-dimensional immutable array containing values of a given function
381
391
* over ranges of integer values starting from `0`.
@@ -430,7 +440,7 @@ object IArray {
430
440
* @return the immutable array with values in range `start, start + 1, ..., end - 1`
431
441
* up to, but excluding, `end`.
432
442
*/
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)
434
444
435
445
/** Returns an immutable array containing equally spaced values in some integer interval.
436
446
*
@@ -439,7 +449,7 @@ object IArray {
439
449
* @param step the increment value of the array (may not be zero)
440
450
* @return the immutable array with values in `start, start + step, ...` up to, but excluding `end`
441
451
*/
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)
443
453
444
454
/** Returns an immutable array containing repeated applications of a function to a start value.
445
455
*
@@ -448,13 +458,16 @@ object IArray {
448
458
* @param f the function that is repeatedly applied
449
459
* @return the immutable array returning `len` values in the sequence `start, f(start), f(f(start)), ...`
450
460
*/
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)
452
462
453
463
/** Returns a decomposition of the array into a sequence. This supports
454
464
* a pattern match like `{ case IArray(x,y,z) => println('3 elements')}`.
455
465
*
456
466
* @param x the selector value
457
467
* @return sequence wrapped in a [[scala.Some ]], if `x` is a Seq, otherwise `None`
458
468
*/
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