0
# Immutable Arrays
1
2
`IArray` provides covariant immutable arrays with rich collection operations, combining the performance characteristics of arrays with immutability guarantees.
3
4
## Core API
5
6
### IArray Type
7
8
```scala { .api }
9
opaque type IArray[+T] = Array[? <: T]
10
```
11
12
An immutable array that has the same representation as `Array[T]` but cannot be updated. Unlike regular arrays, immutable arrays are covariant.
13
14
### Element Access
15
16
```scala { .api }
17
extension (arr: IArray[Byte]) def apply(n: Int): Byte
18
extension (arr: IArray[Short]) def apply(n: Int): Short
19
extension (arr: IArray[Char]) def apply(n: Int): Char
20
extension (arr: IArray[Int]) def apply(n: Int): Int
21
extension (arr: IArray[Long]) def apply(n: Int): Long
22
extension (arr: IArray[Float]) def apply(n: Int): Float
23
extension (arr: IArray[Double]) def apply(n: Int): Double
24
extension [T <: Object](arr: IArray[T]) def apply(n: Int): T
25
extension [T](arr: IArray[T]) def apply(n: Int): T
26
```
27
28
Specialized element access methods for primitive types and generic access for all types.
29
30
### Size Operations
31
32
```scala { .api }
33
extension [T](arr: IArray[T]):
34
def length: Int
35
def size: Int
36
def isEmpty: Boolean
37
def nonEmpty: Boolean
38
def indices: Range
39
```
40
41
Basic size and emptiness checking operations.
42
43
## Transformation Operations
44
45
### Mapping and Filtering
46
47
```scala { .api }
48
extension [T](arr: IArray[T]):
49
def map[U: ClassTag](f: T => U): IArray[U]
50
def flatMap[U: ClassTag](f: T => IterableOnce[U]): IArray[U]
51
def filter(p: T => Boolean): IArray[T]
52
def filterNot(p: T => Boolean): IArray[T]
53
def collect[U: ClassTag](pf: PartialFunction[T, U]): IArray[U]
54
```
55
56
Standard functional operations that return new immutable arrays.
57
58
### Scanning and Folding
59
60
```scala { .api }
61
extension [T](arr: IArray[T]):
62
def scan[U >: T: ClassTag](z: U)(op: (U, U) => U): IArray[U]
63
def scanLeft[U: ClassTag](z: U)(op: (U, T) => U): IArray[U]
64
def scanRight[U: ClassTag](z: U)(op: (T, U) => U): IArray[U]
65
def fold[U >: T](z: U)(op: (U, U) => U): U
66
def foldLeft[U](z: U)(op: (U, T) => U): U
67
def foldRight[U](z: U)(op: (T, U) => U): U
68
```
69
70
Accumulation operations for computing single values or progressive results.
71
72
## Slicing and Selection
73
74
### Element Selection
75
76
```scala { .api }
77
extension [T](arr: IArray[T]):
78
def head: T
79
def headOption: Option[T]
80
def last: T
81
def lastOption: Option[T]
82
def tail: IArray[T]
83
def init: IArray[T]
84
```
85
86
Operations for accessing first, last, and boundary elements.
87
88
### Slicing Operations
89
90
```scala { .api }
91
extension [T](arr: IArray[T]):
92
def take(n: Int): IArray[T]
93
def takeRight(n: Int): IArray[T]
94
def takeWhile(p: T => Boolean): IArray[T]
95
def drop(n: Int): IArray[T]
96
def dropRight(n: Int): IArray[T]
97
def dropWhile(p: T => Boolean): IArray[T]
98
def slice(from: Int, until: Int): IArray[T]
99
def splitAt(n: Int): (IArray[T], IArray[T])
100
```
101
102
Operations for extracting subsequences and splitting arrays.
103
104
## Search and Query Operations
105
106
### Element Search
107
108
```scala { .api }
109
extension [T](arr: IArray[T]):
110
def contains(elem: T): Boolean
111
def indexOf(elem: T, from: Int = 0): Int
112
def lastIndexOf(elem: T, end: Int = arr.length - 1): Int
113
def indexWhere(p: T => Boolean, from: Int = 0): Int
114
def lastIndexWhere(p: T => Boolean, end: Int = arr.length - 1): Int
115
```
116
117
Methods for finding elements and their positions.
118
119
### Predicate Testing
120
121
```scala { .api }
122
extension [T](arr: IArray[T]):
123
def exists(p: T => Boolean): Boolean
124
def forall(p: T => Boolean): Boolean
125
def find(p: T => Boolean): Option[T]
126
def count(p: T => Boolean): Int
127
```
128
129
Testing elements against predicates.
130
131
## Combination Operations
132
133
### Array Combination
134
135
```scala { .api }
136
extension [T](arr: IArray[T]):
137
def ++[U >: T: ClassTag](suffix: IArray[U]): IArray[U]
138
def ++[U >: T: ClassTag](suffix: IterableOnce[U]): IArray[U]
139
def :+[U >: T: ClassTag](x: U): IArray[U]
140
def +:+[U >: T: ClassTag](suffix: IArray[U]): IArray[U]
141
def appended[U >: T: ClassTag](x: U): IArray[U]
142
def appendedAll[U >: T: ClassTag](suffix: IterableOnce[U]): IArray[U]
143
def prepended[U >: T: ClassTag](x: U): IArray[U]
144
def prependedAll[U >: T: ClassTag](prefix: IterableOnce[U]): IArray[U]
145
def concat[U >: T: ClassTag](suffix: IterableOnce[U]): IArray[U]
146
```
147
148
Operations for combining arrays and adding elements.
149
150
### Set-like Operations
151
152
```scala { .api }
153
extension [T](arr: IArray[T]):
154
def diff[U >: T](that: IArray[U]): IArray[T]
155
def diff[U >: T](that: Seq[U]): IArray[T]
156
def intersect[U >: T](that: IArray[U]): IArray[T]
157
def intersect[U >: T](that: Seq[U]): IArray[T]
158
def distinct: IArray[T]
159
def distinctBy[U](f: T => U): IArray[T]
160
```
161
162
## Sorting and Ordering
163
164
```scala { .api }
165
extension [T](arr: IArray[T]):
166
def sorted(using math.Ordering[T]): IArray[T]
167
def sortBy[U](f: T => U)(using math.Ordering[U]): IArray[T]
168
def sortWith(f: (T, T) => Boolean): IArray[T]
169
def reverse: IArray[T]
170
```
171
172
Sorting operations that return new sorted arrays.
173
174
## Conversion Operations
175
176
### Collection Conversions
177
178
```scala { .api }
179
extension [T](arr: IArray[T]):
180
def iterator: Iterator[T]
181
def reverseIterator: Iterator[T]
182
def toSeq: Seq[T]
183
def toList: List[T]
184
def toVector: Vector[T]
185
def toSet: Set[T]
186
```
187
188
Convert to other collection types.
189
190
### Array Conversions
191
192
```scala { .api }
193
extension [T](arr: IArray[T]):
194
@deprecated("Use IArray.genericWrapArray(myIArray).toArray instead")
195
def toArray: Array[T]
196
```
197
198
### Implicit Conversions to ArraySeq
199
200
```scala { .api }
201
implicit def genericWrapArray[T](arr: IArray[T]): ArraySeq[T]
202
implicit def wrapRefArray[T <: AnyRef](arr: IArray[T]): ArraySeq.ofRef[T]
203
implicit def wrapIntArray(arr: IArray[Int]): ArraySeq.ofInt
204
implicit def wrapDoubleIArray(arr: IArray[Double]): ArraySeq.ofDouble
205
implicit def wrapLongIArray(arr: IArray[Long]): ArraySeq.ofLong
206
implicit def wrapFloatIArray(arr: IArray[Float]): ArraySeq.ofFloat
207
implicit def wrapCharIArray(arr: IArray[Char]): ArraySeq.ofChar
208
implicit def wrapByteIArray(arr: IArray[Byte]): ArraySeq.ofByte
209
implicit def wrapShortIArray(arr: IArray[Short]): ArraySeq.ofShort
210
implicit def wrapBooleanIArray(arr: IArray[Boolean]): ArraySeq.ofBoolean
211
implicit def wrapUnitIArray(arr: IArray[Unit]): ArraySeq.ofUnit
212
```
213
214
## Factory Methods
215
216
### Basic Construction
217
218
```scala { .api }
219
object IArray:
220
def empty[T: ClassTag]: IArray[T]
221
def apply[T: ClassTag](xs: T*): IArray[T]
222
def apply(x: Boolean, xs: Boolean*): IArray[Boolean]
223
def apply(x: Byte, xs: Byte*): IArray[Byte]
224
def apply(x: Short, xs: Short*): IArray[Short]
225
def apply(x: Char, xs: Char*): IArray[Char]
226
def apply(x: Int, xs: Int*): IArray[Int]
227
def apply(x: Long, xs: Long*): IArray[Long]
228
def apply(x: Float, xs: Float*): IArray[Float]
229
def apply(x: Double, xs: Double*): IArray[Double]
230
def apply(x: Unit, xs: Unit*): IArray[Unit]
231
```
232
233
### Generation Methods
234
235
```scala { .api }
236
def from[A: ClassTag](it: IterableOnce[A]): IArray[A]
237
def fill[T: ClassTag](n: Int)(elem: => T): IArray[T]
238
def fill[T: ClassTag](n1: Int, n2: Int)(elem: => T): IArray[IArray[T]]
239
def tabulate[T: ClassTag](n: Int)(f: Int => T): IArray[T]
240
def tabulate[T: ClassTag](n1: Int, n2: Int)(f: (Int, Int) => T): IArray[IArray[T]]
241
def range(start: Int, end: Int): IArray[Int]
242
def range(start: Int, end: Int, step: Int): IArray[Int]
243
def iterate[T: ClassTag](start: T, len: Int)(f: T => T): IArray[T]
244
def concat[T: ClassTag](xss: IArray[T]*): IArray[T]
245
```
246
247
### Unsafe Construction
248
249
```scala { .api }
250
def unsafeFromArray[T](s: Array[T]): IArray[T]
251
```
252
253
Convert an array to immutable array without copying. The original array must not be mutated after this call.
254
255
## Usage Examples
256
257
### Basic Operations
258
259
```scala
260
import scala.*
261
262
// Creating arrays
263
val empty = IArray.empty[Int]
264
val numbers = IArray(1, 2, 3, 4, 5)
265
val fromRange = IArray.range(1, 10)
266
val filled = IArray.fill(5)("hello")
267
268
// Element access
269
val first = numbers.head // 1
270
val last = numbers.last // 5
271
val third = numbers(2) // 3
272
val maybeFirst = numbers.headOption // Some(1)
273
274
// Basic properties
275
val size = numbers.length // 5
276
val isEmpty = numbers.isEmpty // false
277
val indices = numbers.indices // Range(0, 1, 2, 3, 4)
278
```
279
280
### Transformations
281
282
```scala
283
val numbers = IArray(1, 2, 3, 4, 5)
284
285
// Mapping
286
val doubled = numbers.map(_ * 2) // IArray(2, 4, 6, 8, 10)
287
val strings = numbers.map(_.toString) // IArray("1", "2", "3", "4", "5")
288
289
// Filtering
290
val evens = numbers.filter(_ % 2 == 0) // IArray(2, 4)
291
val odds = numbers.filterNot(_ % 2 == 0) // IArray(1, 3, 5)
292
293
// Flat mapping
294
val expanded = numbers.flatMap(n => IArray.fill(n)(n))
295
// IArray(1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5)
296
297
// Collecting with partial functions
298
val evenDoubled = numbers.collect { case n if n % 2 == 0 => n * 2 }
299
// IArray(4, 8)
300
```
301
302
### Slicing and Selection
303
304
```scala
305
val data = IArray("a", "b", "c", "d", "e")
306
307
// Taking and dropping
308
val firstThree = data.take(3) // IArray("a", "b", "c")
309
val lastTwo = data.takeRight(2) // IArray("d", "e")
310
val skipFirst = data.drop(1) // IArray("b", "c", "d", "e")
311
val skipFirstTwo = data.drop(2) // IArray("c", "d", "e")
312
313
// Conditional taking/dropping
314
val letters = IArray("a", "b", "c", "1", "2")
315
val onlyLetters = letters.takeWhile(_.forall(_.isLetter)) // IArray("a", "b", "c")
316
317
// Slicing
318
val middle = data.slice(1, 4) // IArray("b", "c", "d")
319
val (left, right) = data.splitAt(2) // (IArray("a", "b"), IArray("c", "d", "e"))
320
```
321
322
### Search and Query
323
324
```scala
325
val values = IArray(10, 20, 30, 20, 40)
326
327
// Finding elements
328
val contains20 = values.contains(20) // true
329
val firstIndex = values.indexOf(20) // 1
330
val lastIndex = values.lastIndexOf(20) // 3
331
val indexOver25 = values.indexWhere(_ > 25) // 2
332
333
// Predicate testing
334
val hasLarge = values.exists(_ > 35) // true
335
val allPositive = values.forall(_ > 0) // true
336
val firstLarge = values.find(_ > 25) // Some(30)
337
val countLarge = values.count(_ > 25) // 2
338
```
339
340
### Combining Arrays
341
342
```scala
343
val arr1 = IArray(1, 2, 3)
344
val arr2 = IArray(4, 5, 6)
345
val list = List(7, 8, 9)
346
347
// Concatenation
348
val combined = arr1 ++ arr2 // IArray(1, 2, 3, 4, 5, 6)
349
val withList = arr1 ++ list // IArray(1, 2, 3, 7, 8, 9)
350
351
// Adding single elements
352
val withZero = 0 +: arr1 // IArray(0, 1, 2, 3)
353
val withTen = arr1 :+ 10 // IArray(1, 2, 3, 10)
354
355
// Prepending/appending collections
356
val prefixed = IArray(-2, -1) ++: arr1 // IArray(-2, -1, 1, 2, 3)
357
val suffixed = arr1 :++ IArray(10, 11) // IArray(1, 2, 3, 10, 11)
358
```
359
360
### Sorting and Ordering
361
362
```scala
363
val unsorted = IArray(3, 1, 4, 1, 5, 9, 2)
364
365
// Natural ordering
366
val sorted = unsorted.sorted // IArray(1, 1, 2, 3, 4, 5, 9)
367
368
// Custom ordering
369
val descending = unsorted.sortWith(_ > _) // IArray(9, 5, 4, 3, 2, 1, 1)
370
371
// Sort by transformation
372
val words = IArray("hello", "hi", "world", "a")
373
val byLength = words.sortBy(_.length) // IArray("a", "hi", "hello", "world")
374
375
// Reverse
376
val reversed = sorted.reverse // IArray(9, 5, 4, 3, 2, 1, 1)
377
```
378
379
### Advanced Operations
380
381
```scala
382
val data = IArray(1, 2, 3, 4, 5)
383
384
// Scanning (cumulative operations)
385
val cumSum = data.scanLeft(0)(_ + _) // IArray(0, 1, 3, 6, 10, 15)
386
val cumProduct = data.scanLeft(1)(_ * _) // IArray(1, 1, 2, 6, 24, 120)
387
388
// Folding (reduction to single value)
389
val sum = data.foldLeft(0)(_ + _) // 15
390
val product = data.foldRight(1)(_ * _) // 120
391
392
// Grouping and partitioning
393
val mixed = IArray(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
394
val (evens, odds) = mixed.partition(_ % 2 == 0)
395
// evens: IArray(2, 4, 6, 8, 10), odds: IArray(1, 3, 5, 7, 9)
396
397
// Set operations
398
val set1 = IArray(1, 2, 3, 4)
399
val set2 = IArray(3, 4, 5, 6)
400
val intersection = set1.intersect(set2) // IArray(3, 4)
401
val difference = set1.diff(set2) // IArray(1, 2)
402
val unique = IArray(1, 2, 2, 3, 3, 3).distinct // IArray(1, 2, 3)
403
```
404
405
### Conversion and Iteration
406
407
```scala
408
val arr = IArray("a", "b", "c", "d")
409
410
// To other collections
411
val list = arr.toList // List("a", "b", "c", "d")
412
val vector = arr.toVector // Vector("a", "b", "c", "d")
413
val set = arr.toSet // Set("a", "b", "c", "d")
414
415
// Iteration
416
arr.foreach(println) // Prints each element
417
val iterator = arr.iterator // Iterator for traversal
418
val reverseIter = arr.reverseIterator // Reverse iterator
419
420
// Side effects
421
arr.foreach(x => println(s"Element: $x"))
422
```
423
424
### Unsafe Operations and Performance
425
426
```scala
427
// Unsafe conversion from mutable array (use with caution)
428
val mutableArray = Array(1, 2, 3, 4, 5)
429
val immutableArray = IArray.unsafeFromArray(mutableArray)
430
// Warning: Do not modify mutableArray after this point!
431
432
// Efficient conversion through ArraySeq
433
val properConversion = IArray.genericWrapArray(immutableArray).toArray
434
```
435
436
IArray provides a complete immutable array implementation with excellent performance characteristics and a rich API that integrates seamlessly with Scala's collection library while maintaining type safety and immutability guarantees.