Sequences

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against Scala 3.10 (3.10.0, released 2026-10-12) and every example was compiled and run with Scala 3.10.0 through Scala CLI on JDK 26 (checked on 2026-10-15). Sequences are part of the 2.13 collections library that Scala 3 shares, so nothing here changed on the 3.9 LTS (3.9.0, 2026-09-03) or 3.3 LTS (3.3.8) lines, apart from one compiler message listed in Scala 3.9 LTS versus 3.10.

A sequence is an Iterable whose elements have a fixed position: xs(0) is the first element and the elements keep the order in which they were put. The position makes some questions cheap (what is the third element?) and others expensive (insert an element at index 1000), and the many sequence classes exist because each one makes a different trade-off. The overall map is on Collections Overview; the sets and maps are on Sets & Maps; map, filter, folds and friends are on Collection Operations; the cost tables are discussed on Collection Performance & Equality. The examples use the trailhead scenario, where a hike is a sequence of stops. The Java counterparts are in the Java Collections Framework.

Which sequence

Class Package and kind Use it for

List

scala.collection.immutable, linear, immutable

Recursion, pattern matching with ::, prepending, short sequences processed from the front.

Vector

scala.collection.immutable, indexed, immutable

The general-purpose immutable sequence: effectively constant time indexing, update, prepend and append.

Range

scala.collection.immutable, indexed, immutable

Arithmetic progressions (1 to 10, 0 until n by 2); stores three numbers, not the elements.

ArraySeq

immutable and mutable, indexed

A Seq that is an array underneath; the bridge between Array and Seq.

Queue

immutable, linear

FIFO with amortised constant time enqueue and dequeue.

ArrayBuffer

scala.collection.mutable, indexed, mutable

A growable array: append in amortised constant time, index in constant time.

ListBuffer

scala.collection.mutable, mutable

Building a List by appending; toList is constant time.

ArrayDeque

scala.collection.mutable, indexed, mutable

Fast add and remove at both ends; use it for queues, deques and stacks.

Array

JVM array

Fixed size, primitive speed, Java interop.

IArray

Scala 3, opaque type over an array

A read-only view of an array in the type system.

StringBuilder

scala.collection.mutable, mutable

Building a String in place.

The rest of the page takes them one by one. The cost numbers come from the performance table in the official 2.13 guide, with C for constant, eC for effectively constant, aC for amortised constant and L for linear in the size:

Class head tail apply update prepend append

List

C

C

L

L

C

L

Vector

eC

eC

eC

eC

eC

eC

ArraySeq (immutable)

C

L

C

L

L

L

Queue (immutable)

aC

aC

L

L

C

C

Range

C

C

C

-

-

-

ArrayBuffer

C

L

C

C

L

aC

ListBuffer

C

L

L

L

C

C

ArrayDeque

C

L

C

C

aC

aC

Array

C

L

C

C

-

-

List

List is an immutable singly linked list built from two cases: Nil, the empty list, and :: ("cons"), a cell holding a head and a tail that is itself a List. Because a cell never changes, any list can share its tail with other lists: x :: xs allocates a single new cell and reuses xs in full. That makes prepending, head and tail constant time, and it makes indexing, length, last and appending linear.

//> using scala 3.10.0
package trailhead

@main def lists(): Unit =
  val stops = List("Hut", "Ridge", "Pass")
  println(stops.head)
  println(stops.tail)
  println("Start" :: stops)
  println(stops :+ "Summit")
  println(List(0) ::: stops.map(_.length))
  println(stops.last)
  println(stops(1))
  println(Nil)
  println(1 :: 2 :: Nil)
  stops match
    case first :: rest => println(s"$first then $rest")
    case Nil => println("empty")
  val shared = "A" :: stops
  val other = "B" :: stops
  println(shared.tail eq other.tail)
  println(List.fill(3)("x"))
  println(List.range(0, 10, 3))
  println(stops.reverse)
  println(stops.length)
Hut
List(Ridge, Pass)
List(Start, Hut, Ridge, Pass)
List(Hut, Ridge, Pass, Summit)
List(0, 3, 5, 4)
Pass
Ridge
List()
List(1, 2)
Hut then List(Ridge, Pass)
true
List(x, x, x)
List(0, 3, 6, 9)
List(Pass, Ridge, Hut)
3

Reading the output: :: and ::: end with a colon, so they are right-associative and are methods of the right operand ("Start" :: stops is stops.::("Start")); 1 :: 2 :: Nil is Nil.::(2).::(1). The line printing true shows that two lists built with "A" :: stops and "B" :: stops share the very same tail object (eq is reference equality). :+ appends but copies the whole list, so a loop that appends to a List is quadratic; prepend and reverse at the end instead, or use a ListBuffer.

The natural fit for List is recursion over its front, with the pattern case first :: rest, covered on Pattern Matching and Recursion & Tail Calls. It is a LinearSeq: the cost of xs(i) grows with i, so avoid index loops over a List.

Vector

Vector is the immutable sequence for everything that is not "recursion over the front". It is a wide, shallow tree (a trie with branching factor 32), so all of apply, updated, :+ and +: take effectively constant time, and an "update" copies only a handful of small arrays and shares the rest.

//> using scala 3.10.0
package trailhead

@main def vectors(): Unit =
  val v = Vector(10, 20, 30, 40)
  println(v(2))
  println(v.updated(1, 99))
  println(v :+ 50)
  println(0 +: v)
  println(v)
  println(v.take(2))
  println(v.last)
  val big = Vector.tabulate(1000)(identity)
  println(big.updated(500, -1)(500))
  println(big(500))
  println(Vector.empty[Int])
  println(v.getClass.getSimpleName)
  println(big.getClass.getSimpleName)
30
Vector(10, 99, 30, 40)
Vector(10, 20, 30, 40, 50)
Vector(0, 10, 20, 30, 40)
Vector(10, 20, 30, 40)
Vector(10, 20)
40
-1
500
Vector()
Vector1
Vector2

The class names at the end (Vector1, Vector2) are the internal depth: a Vector of up to 32 elements is a single array, up to 1024 elements two levels, and so on up to six levels. The updated vector big.updated(500, -1) has a different element at index 500 while big itself is unchanged.

When in doubt about which immutable sequence to use, use Vector. Use List when you consume the sequence from the front, and Vector when you index into it or append to it. Seq(...) builds a List by default; if you want a Vector write Vector(...) or declare the type IndexedSeq.

ArrayBuffer and ListBuffer

When you build a sequence in a loop, or need in-place updates, use a mutable buffer. A Buffer is a growable Seq with += and ++= to append, prepend and insert to add elsewhere, -= and remove to delete, and in-place variants of the transformations (filterInPlace, mapInPlace, dropInPlace).

//> using scala 3.10.0
import scala.collection.mutable.{ArrayBuffer, ListBuffer}
@main def buffers(): Unit =
  val buf = ArrayBuffer("a", "b")
  buf += "c"
  buf ++= List("d", "e")
  println(buf)
  buf.prepend("z")
  buf.insert(2, "m")
  println(buf)
  buf -= "a"
  buf.remove(0)
  println(buf)
  buf(0) = "Q"
  println(buf)
  println(buf.toList)
  buf.clear()
  println(buf)
  val lb = ListBuffer(1, 2, 3)
  lb += 4
  lb.prepend(0)
  println(lb)
  val result = lb.toList
  println(result)
  val b = List.newBuilder[Int]
  b += 1
  b += 2
  println(b.result())
  val ab = ArrayBuffer(1,2,3,4,5,6)
  ab.filterInPlace(_ % 2 == 0)
  println(ab)
  ab.mapInPlace(_ * 10)
  println(ab)
  ab.dropInPlace(1)
  println(ab)
ArrayBuffer(a, b, c, d, e)
ArrayBuffer(z, a, m, b, c, d, e)
ArrayBuffer(m, b, c, d, e)
ArrayBuffer(Q, b, c, d, e)
List(Q, b, c, d, e)
ArrayBuffer()
ListBuffer(0, 1, 2, 3, 4)
List(0, 1, 2, 3, 4)
List(1, 2)
ArrayBuffer(2, 4, 6)
ArrayBuffer(20, 40, 60)
ArrayBuffer(40, 60)

ArrayBuffer keeps the elements in an array that doubles when full, so append is amortised constant time and indexing is constant time, while prepend, insert and remove in the middle shift elements and are linear. ListBuffer keeps a linked list with a pointer to its last cell, so prepend and append are constant time and toList is constant time too (it hands over the list and marks the buffer as shared); indexing is linear. The Builder returned by List.newBuilder[Int] is the generic form of the same pattern: += elements, then result().

A buffer is mutable, so a val buf = ArrayBuffer(...) still lets you change buf: val fixes the reference, not the contents. Immutable collections with val cannot be changed at all, which is why they need no -=:

//> using scala 3.10.0
import scala.collection.mutable
@main def bad(): Unit =
  val xs: Seq[Int] = List(1,2,3)
  val buf: mutable.Buffer[Int] = xs
  val l = List(1, 2)
  l += 3
-- [E007] Type Mismatch Error: immbad.scala:5:33
5 |  val buf: mutable.Buffer[Int] = xs
  |                                 ^^
  |                            Found:    (xs : Seq[Int])
  |                            Required: scala.collection.mutable.Buffer[Int]
  |
  | longer explanation available when compiling with `-explain`
-- [E008] Not Found Error: immbad.scala:7:4
7 |  l += 3
  |  ^^^^
  |value += is not a member of List[Int] - did you mean l.!=? or perhaps l.++ or l.+: or l.==?
2 errors found
Compilation failed

(The compiler lists look-alike members because there is no += on List; the intended fix is a mutable collection, a var with xs :+= x, or building a new list.) Seq is also not assignable to mutable.Buffer, the first error above, for the reason explained in Pitfalls.

Array, ArraySeq and IArray

Array[T] is the JVM array. It is mutable, has a fixed size, is accessed and updated with parentheses (a(0) = 10, which the compiler turns into a.update(0, 10)), and unlike every other collection it is reified: Array[Int] is a real int[] and Array[String] is a real String[]. It is invariant in T (see Variance), where Java arrays are covariant. It is not a Seq; the operations (map, sorted, toList, …​) come from an implicit conversion to an array wrapper, so they work on arrays too and map returns an Array.

//> using scala 3.10.0
import scala.collection.immutable.ArraySeq
@main def arrays(): Unit =
  val a = Array(1, 2, 3)
  a(0) = 10
  println(a.mkString(","))
  println(a.length)
  println(a.toList)
  val b = Array.ofDim[Int](2, 3)
  b(1)(2) = 7
  println(b.map(_.mkString("[", " ", "]")).mkString(" "))
  println(a.map(_ * 2).mkString(","))
  println(a.sorted.mkString(","))
  println(Array(1, 2) == Array(1, 2))
  println(Array(1, 2).sameElements(Array(1, 2)))
  println(Array(1, 2).toSeq == Array(1, 2).toSeq)
  println(a.getClass.getSimpleName)
  val strs = Array("a", "b")
  println(strs.getClass.getSimpleName)
  val s: ArraySeq[Int] = ArraySeq(1, 2, 3)
  println(s)
  println(s.getClass.getName)
  val w = ArraySeq.unsafeWrapArray(a)
  a(1) = 99
  println(w)
  println(a.length)
  println(a.sum)
10,2,3
3
List(10, 2, 3)
[0 0 0] [0 0 7]
20,4,6
2,3,10
false
true
true
int[]
String[]
ArraySeq(1, 2, 3)
scala.collection.immutable.ArraySeq$ofInt
ArraySeq(10, 99, 3)
3
112

Points to note in the output: printing an Array prints an identity string, so go through mkString or toList; Array(1, 2) == Array(1, 2) is false because arrays use reference equality, while sameElements and a conversion to Seq compare contents; Array.ofDim[Int](2, 3) creates a two-dimensional array; and ArraySeq.unsafeWrapArray(a) shares storage with a, which the printed ArraySeq(10, 99, 3) shows after a(1) = 99. For performance with primitives, Array[Int] avoids boxing completely.

Because an array’s element type is needed at run time to create it, generic code that creates arrays needs a ClassTag:

//> using scala 3.10.0
def firstOf[T](xs: Array[T]): T = xs(0)
def make[T](x: T): Array[T] = Array(x)

@main def generic(): Unit =
  println(firstOf(Array("x")))
  println(make(1))
-- [E172] Type Error: classtag.scala:3:38
3 |def make[T](x: T): Array[T] = Array(x)
  |                                      ^
  |No reflect.ClassTag available for T. You can fix this by changing [T] to [T: reflect.ClassTag].
1 error found
Compilation failed

The fix is a context bound, def make[T: reflect.ClassTag](x: T): Array[T], as the 3.10 message says (3.9.0 and 3.3.8 only print "No ClassTag available for T"). Generic code that is happy with any sequence should use Vector, List or ArraySeq.

IArray[T] (Scala 3) is an immutable array: an opaque type whose only difference from Array is the missing update method. At run time it is a plain JVM array with no wrapper, which makes it the cheapest way to hold a read-only table of primitives.

//> using scala 3.10.0
@main def ia(): Unit =
  val a = IArray(3, 1, 2)
  val b = a.map(_ + 1)
  println(b.mkString(","))
  println(a.sorted.mkString(","))
  println(a(0))
  println(a.length)
  println(a.toList)
  val c = a ++ IArray(9)
  println(c.mkString(","))
  println(a.updated(0, 7).mkString(","))
4,2,3
1,2,3
3
3
List(3, 1, 2)
3,1,2,9
7,1,2

updated, ++ and map return fresh IArray values, as for any collection; map on a primitive array needs a ClassTag for the result, which is found automatically for Int. Mutation through the type is rejected:

//> using scala 3.10.0
@main def bad(): Unit =
  val a = IArray(3, 1, 2)
  a(0) = 5
-- [E008] Not Found Error: iarraybad.scala:4:2
4 |  a(0) = 5
  |  ^
  |  value update is not a member of IArray[Int]
1 error found
Compilation failed

Range

A Range is an immutable sequence of integers described by a start, an end and a step. It is an IndexedSeq that never stores the numbers: (1 to 1000000000).size is computed instantly. to includes the end, until excludes it, by sets the step (negative to count down), and the same syntax works for Char and, through BigDecimal, for decimals.

//> using scala 3.10.0
package trailhead

@main def ranges(): Unit =
  val r = 1 to 5
  println(r)
  println(r.toList)
  println(1 until 5)
  println(10 to 1 by -3)
  println((1 to 3).map(_ * 2))
  println((1 to 1000000000).size)
  println(r.getClass.getName)
  println(BigDecimal(1.0) to BigDecimal(2.0) by BigDecimal(0.5))
  println('a' to 'e')
  println((1 to 0).isEmpty)
Range 1 to 5
List(1, 2, 3, 4, 5)
Range 1 until 5
Range 10 to 1 by -3
Vector(2, 4, 6)
1000000000
scala.collection.immutable.Range$Inclusive
NumericRange 1.0 to 2.0 by 0.5
NumericRange a to e
true

Ranges are the standard way to loop (for i <- 0 until n, see Conditionals & Loops) and are used in a for comprehension (For Comprehensions). A Floating point has no Range: 1.0 to 2.0 by 0.5 is rejected with "value to is not a member of Double", so write the bounds as BigDecimal (which gives a NumericRange, as in the output above) or use a plain loop. map on a Range returns a Vector, as the fifth output line shows.

Queue, ArrayDeque and StringBuilder

Scala has an immutable Queue (two lists, so enqueue and dequeue are amortised constant time) and a mutable scala.collection.mutable.Queue, which since 2.13 is a thin subclass of ArrayDeque. ArrayDeque is the mutable double-ended queue implemented as a circular buffer; it is also the right choice for a stack (append and removeLast). StringBuilder builds a String in place and is the mutable Seq[Char]; it is the only mutable collection name in scope without an import.

//> using scala 3.10.0
package trailhead

import scala.collection.immutable.Queue
import scala.collection.mutable

@main def queues(): Unit =
  val q = Queue(1, 2)
  val q2 = q.enqueue(3)
  val (head, rest) = q2.dequeue
  println(s"$head $rest")
  println(q2.enqueueAll(List(4, 5)))
  val dq = mutable.ArrayDeque(1, 2, 3)
  dq.prepend(0)
  dq.append(4)
  println(dq)
  println(dq.removeHead())
  println(dq.removeLast())
  println(dq)
  println(dq(1))
  val mq = mutable.Queue("a", "b")
  mq.enqueue("c")
  println(mq.dequeue())
  println(mq)
  val sb = StringBuilder()
  sb.append("hike").append(' ').append(42)
  sb ++= "!"
  println(sb)
  println(sb.length)
  sb.insert(0, ">> ")
  println(sb.toString)
  println(sb.getClass.getName)
1 Queue(2, 3)
Queue(1, 2, 3, 4, 5)
ArrayDeque(0, 1, 2, 3, 4)
0
4
ArrayDeque(1, 2, 3)
2
a
Queue(b, c)
hike 42!
8
>> hike 42!
scala.collection.mutable.StringBuilder

The immutable Queue returns a new queue from enqueue and a pair (head, rest) from dequeue, so a state machine over it is a fold. The mutable queue returns the element and changes itself. ArrayDeque supports prepend and append in amortised constant time and removeHead and removeLast, plus constant time indexing. scala.collection.mutable.Stack exists (it was deprecated in 2.12 and reimplemented in 2.13) but ArrayDeque is the usual choice.

What every Seq can do

Seq adds position-based operations to the Iterable vocabulary: apply, isDefinedAt, lift, indexOf, contains, padTo, updated, patch, prepended/appended, sorted, reverse/reverseIterator, search (binary search in a sorted sequence), startsWith, combinations, diff, intersect and concat. A few are worth seeing in action. Note the last line: s(10) on a three element Seq throws.

//> using scala 3.10.0
package trailhead

@main def seqs(): Unit =
  val s = Seq(1, 2, 3)
  println(s.lengthCompare(2))
  println(s.isDefinedAt(5))
  println(s.lift(5))
  println(s.lift(1))
  println(s.indexOf(3))
  println(s.contains(2))
  println(s.padTo(5, 0))
  println(s.updated(0, 7))
  println(s.patch(1, List(9, 9), 1))
  println(s.prepended(0))
  println(s.appended(4))
  println(s.sorted(using Ordering[Int].reverse))
  println(s.reverseIterator.toList)
  println(s.search(2))
  println(s.startsWith(Seq(1, 2)))
  println(s.combinations(2).toList)
  println(s.diff(Seq(1)))
  println(s.intersect(Seq(2, 3, 4)))
  println(s.concat(Seq(4)))
  println(s(10))
1
false
None
Some(2)
2
true
List(1, 2, 3, 0, 0)
List(7, 2, 3)
List(1, 9, 9, 3)
List(0, 1, 2, 3)
List(1, 2, 3, 4)
List(3, 2, 1)
List(3, 2, 1)
Found(1)
true
List(List(1, 2), List(1, 3), List(2, 3))
List(2, 3)
List(2, 3)
List(1, 2, 3, 4)
Exception in thread "main" java.lang.IndexOutOfBoundsException: 10
	at scala.collection.LinearSeqOps.apply(LinearSeq.scala:141)
	at scala.collection.LinearSeqOps.apply$(LinearSeq.scala:45)
	at scala.collection.immutable.List.apply(List.scala:83)

lift is the safe version of apply: it returns an Option instead of throwing, which is the idiomatic way to read a position that may not exist. search returns Found(index) or InsertionPoint(index). union is deprecated since 2.13.0 in favour of concat ("Use concat instead"). The exception from s(10) on a List is an IndexOutOfBoundsException; the stack frames show that List reaches the element through LinearSeqOps.apply, walking the list.

Indexed versus linear

Seq has two refinements that declare the performance contract. IndexedSeq promises fast random access and length (Vector, Range, ArrayBuffer, ArrayDeque, ArraySeq, String through an implicit wrapper), and LinearSeq promises fast head, tail and isEmpty (List, LazyList, Queue). If a method indexes into its argument, ask for an IndexedSeq; if it only walks from the front, ask for Seq. A plain Seq gives no promise at all, and passing a List to code that loops with xs(i) is a classic quadratic trap.

Pattern matching on sequences

Every Seq can be matched with the Seq(...) extractor pattern, including a trailing wildcard that captures the rest, and the extractors +: and :+ match the first and last element (on Vector and other sequences; :: is List only). The extractor machinery is described on Extractors & Pattern Bindings.

//> using scala 3.10.0
package trailhead

import scala.collection.mutable

def describe(xs: Seq[String]): String = xs match
  case Seq() => "no stops"
  case Seq(only) => s"one stop: $only"
  case Seq(first, second) => s"two stops: $first, $second"
  case Seq(first, rest*) => s"$first and ${rest.size} more"

def lastOf[A](xs: Vector[A]): Option[A] = xs match
  case init :+ last => Some(last)
  case _ => None

@main def patterns(): Unit =
  println(describe(Nil))
  println(describe(List("Hut")))
  println(describe(Vector("Hut", "Ridge")))
  println(describe(List("Hut", "Ridge", "Pass", "Summit")))
  println(lastOf(Vector(1, 2, 3)))
  println(lastOf(Vector.empty[Int]))
  val stack = mutable.ArrayDeque[Int]()
  stack.append(1)
  stack.append(2)
  println(stack.removeLast())
no stops
one stop: Hut
two stops: Hut, Ridge
Hut and 3 more
Some(3)
None
2

In case Seq(first, rest*), rest* binds the remaining elements as a Seq. In Scala 2 the same pattern was written rest @ _*; the Scala 3 spelling is rest*, and the old spelling still compiled without any warning on 3.10.0 (checked with and without -source:future). The pattern init : last+ is cheap on a Vector; on a List it would walk the entire list.

Comparison with Java and Kotlin

Aspect Scala Java Kotlin

Default list

List: immutable singly linked list.

ArrayList: mutable growable array; LinkedList is rarely right.

List: read-only view, usually an ArrayList.

Growable sequence

ArrayBuffer, ListBuffer, ArrayDeque.

ArrayList, ArrayDeque.

ArrayList, ArrayDeque.

Immutable general purpose

Vector, List.

List.of(...), an unmodifiable list with no structural sharing.

Read-only List has no structural sharing either.

Append to an immutable list

xs :+ x: returns a new collection.

Not available; copy.

xs + x: returns a new list.

Arrays

Array[T] invariant; operations come from an implicit conversion; IArray for read-only.

int[] and T[], with covariant arrays and the Arrays helper class.

Array<T> and IntArray; extension functions.

Ranges

1 to 10, 0 until n by 2: a real Seq.

IntStream.rangeClosed(1, 10): a stream, not a collection.

1..10, 0 until n step 2: an IntRange, which is an Iterable of integers.

Safe indexing

xs.lift(i) returns an Option.

xs.get(i) throws IndexOutOfBoundsException.

getOrNull(i) and getOrElse(i) { ... }.

Where Java code uses Optional the Scala code uses Option, which is why lift is natural; see Optional and Option & Null Safety for that side, and Kotlin Null Safety for the Kotlin approach with ?.

Pitfalls

  • Seq is List by default. Seq(1, 2, 3) builds a List, so indexed access in a loop is quadratic. Write Vector(...) when you index.

  • += on an immutable collection. The compiler error is value += is not a member of List[Int]; there is no in-place growth on an immutable collection.

  • Mutable collections do not conform to Seq. Passing an ArrayBuffer where Seq is expected fails with E007 (Found: ArrayBuffer[Int], Required: Seq[Int]). Accept scala.collection.Seq or convert with toSeq.

  • Arrays compare by reference and print as identity strings; wrap or convert them before comparing or printing.

  • Generic arrays need a ClassTag (error E172 shown above).

  • ArraySeq.unsafeWrapArray aliases the array. Never mutate the array afterwards unless you want the ArraySeq to change too.

  • Slow loops over List. xs(i), xs.length, xs.last and :+ are linear.

  • Out-of-range index throws IndexOutOfBoundsException; use lift, isDefinedAt or applyOrElse.

  • Appending in a ListBuffer after calling toList. toList is constant time, but later modifications of the buffer copy it first; prefer to finish building before converting.

Where the books are outdated

  • Material for Scala 2.12 and earlier describes scala.Seq as scala.collection.Seq (so mutable collections conform) and shows Stream as the lazy list; since 2.13 Seq is immutable and Stream is deprecated in favour of LazyList.

  • Material from before 2.13 describes WrappedArray; in 2.13 and 3 the wrappers are ArraySeq.ofInt and friends, as the ArraySeq$ofInt class name in the output shows.

  • Seq.union is deprecated since 2.13.0; use concat.

  • IArray and opaque types do not exist in Scala 2; a Scala 3 book chapter on them (or the reference on opaque types) is the only source of the update-less array.

Scala 3.9 LTS versus 3.10

The sequence classes and operations behave identically on 3.3.8, 3.9.0 and 3.10.0: every program on this page was compiled and run on all three and produced the same output. The one visible difference is the compiler message for a missing ClassTag when creating a generic array, error E172: 3.10.0 says "No reflect.ClassTag available for T. You can fix this by changing [T] to [T: reflect.ClassTag]." while 3.9.0 and 3.3.8 only say "No ClassTag available for T". The other error messages on this page (E007, E008 for IArray, and E008 for +=) are identical on the three versions.