CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-scala-lang-modules--scala-collection-compat

A library that makes some Scala 2.13 APIs available on Scala 2.11 and 2.12, facilitating cross-building Scala 2.13 and 3.0 code on older versions

Pending
Overview
Eval results
Files

backported-collections.mddocs/

Backported Collections

Scala 2.13 collection types made available on Scala 2.11 and 2.12, enabling forward compatibility for code that targets multiple Scala versions.

LazyList

A lazy, immutable linked list that evaluates elements only when needed. LazyList is the replacement for Stream in Scala 2.13+.

LazyList Class

final class LazyList[+A] private (private[this] var lazyState: () => LazyList.State[A])
    extends AbstractSeq[A] with LinearSeq[A] with LinearSeqOptimized[A, LazyList[A]] {
  
  def head: A
  def tail: LazyList[A]
  def isEmpty: Boolean
  def force: this.type
  def knownSize: Int
}

Core Methods

head / tail
def head: A
def tail: LazyList[A]

Access the first element and remaining elements. Elements are computed lazily.

Usage:

val lazyList = LazyList(1, 2, 3, 4, 5)
val first = lazyList.head      // 1
val rest = lazyList.tail       // LazyList(2, 3, 4, 5)
isEmpty
def isEmpty: Boolean

Check if the lazy list is empty.

force
def force: this.type

Force evaluation of all elements in the lazy list. Detects and handles cycles.

Usage:

val infinite = LazyList.from(1)
// infinite.force  // Would not terminate
val finite = LazyList(1, 2, 3).force  // Forces evaluation of all elements
knownSize
def knownSize: Int

Returns the known size without forcing evaluation, or -1 if unknown.

LazyList Companion Object

object LazyList extends SeqFactory[LazyList] {
  def empty[A]: LazyList[A]
  def apply[A](elems: A*): LazyList[A]
  def from[A](coll: GenTraversableOnce[A]): LazyList[A]
  def iterate[A](start: => A)(f: A => A): LazyList[A]
  def continually[A](elem: => A): LazyList[A]
  def unfold[A, S](init: S)(f: S => Option[(A, S)]): LazyList[A]
  def concat[A](xss: collection.Iterable[A]*): LazyList[A]
  def from(start: Int): LazyList[Int]
  def from(start: Int, step: Int): LazyList[Int]
  def fill[A](n: Int)(elem: => A): LazyList[A]
  def tabulate[A](n: Int)(f: Int => A): LazyList[A]
}

Factory Methods

from
def from[A](coll: GenTraversableOnce[A]): LazyList[A]
def from(start: Int): LazyList[Int]
def from(start: Int, step: Int): LazyList[Int]

Create a LazyList from a collection or generate an infinite sequence of integers.

Usage:

LazyList.from(List(1, 2, 3))        // LazyList(1, 2, 3)
LazyList.from(1)                    // LazyList(1, 2, 3, 4, ...)
LazyList.from(0, 2)                 // LazyList(0, 2, 4, 6, ...)
iterate
def iterate[A](start: => A)(f: A => A): LazyList[A]

Create an infinite LazyList by repeatedly applying a function.

Usage:

LazyList.iterate(1)(_ * 2)          // LazyList(1, 2, 4, 8, 16, ...)
LazyList.iterate("a")(_ + "a")      // LazyList("a", "aa", "aaa", ...)
continually
def continually[A](elem: => A): LazyList[A]

Create an infinite LazyList with the same element repeated.

Usage:

LazyList.continually(42)            // LazyList(42, 42, 42, ...)
LazyList.continually(scala.util.Random.nextInt())  // Random numbers
unfold
def unfold[A, S](init: S)(f: S => Option[(A, S)]): LazyList[A]

Create a LazyList by unfolding a state with a generating function.

Usage:

// Generate Fibonacci sequence
LazyList.unfold((0, 1)) { case (a, b) =>
  Some((a, (b, a + b)))
}  // LazyList(0, 1, 1, 2, 3, 5, 8, ...)

// Generate sequence up to a limit
LazyList.unfold(0) { n =>
  if (n < 10) Some((n, n + 1)) else None
}  // LazyList(0, 1, 2, ..., 9)
fill / tabulate
def fill[A](n: Int)(elem: => A): LazyList[A]
def tabulate[A](n: Int)(f: Int => A): LazyList[A]

Create finite LazyLists with repeated elements or computed elements.

Usage:

LazyList.fill(5)("x")               // LazyList("x", "x", "x", "x", "x")  
LazyList.tabulate(5)(n => n * n)    // LazyList(0, 1, 4, 9, 16)

LazyList Construction Operators

cons Object

object cons {
  def apply[A](hd: => A, tl: => LazyList[A]): LazyList[A]
  def unapply[A](xs: LazyList[A]): Option[(A, LazyList[A])]
}

Alternative construction and pattern matching for LazyList.

#:: Operator

object #:: {
  def unapply[A](s: LazyList[A]): Option[(A, LazyList[A])]
}

implicit def toDeferrer[A](l: => LazyList[A]): Deferrer[A]

final class Deferrer[A] private[LazyList] (private val l: () => LazyList[A]) {
  def #::[B >: A](elem: => B): LazyList[B]
  def #:::[B >: A](prefix: LazyList[B]): LazyList[B]
}

Lazy cons operators for constructing LazyLists.

Usage:

val lazyList = 1 #:: 2 #:: 3 #:: LazyList.empty
val infinite = 1 #:: infinite.map(_ + 1)

// Pattern matching
lazyList match {
  case head #:: tail => println(s"Head: $head, Tail: $tail")
  case _ => println("Empty")
}

ArraySeq

An immutable sequence backed by an array, providing efficient indexed access with a small memory footprint.

ArraySeq Class

abstract class ArraySeq[+T] extends AbstractSeq[T] with IndexedSeq[T] 
    with IndexedSeqOptimized[T, ArraySeq[T]] {
  
  def length: Int
  def apply(index: Int): T
  def unsafeArray: Array[T]
  def clone(): ArraySeq[T]
}

Core Methods

length / apply
def length: Int
def apply(index: Int): T

Standard indexed sequence operations with O(1) access time.

unsafeArray
def unsafeArray: Array[T]

Direct access to the underlying array. Use with caution as it breaks immutability guarantees if modified.

clone
def clone(): ArraySeq[T]

Create a deep copy of the ArraySeq, including cloning the underlying array.

ArraySeq Companion Object

object ArraySeq {
  def empty[T <: AnyRef]: ArraySeq[T]
  def apply[T](elems: T*)(implicit elemTag: ClassTag[T]): ArraySeq[T]
  def unsafeWrapArray[T](x: Array[T]): ArraySeq[T]
  def newBuilder[T](implicit elemTag: ClassTag[T]): Builder[T, ArraySeq[T]]
  def unapplySeq[T](seq: ArraySeq[T]): Some[ArraySeq[T]]
}

Factory Methods

apply
def apply[T](elems: T*)(implicit elemTag: ClassTag[T]): ArraySeq[T]

Create an ArraySeq from variable arguments.

Usage:

ArraySeq(1, 2, 3, 4, 5)            // ArraySeq[Int]
ArraySeq("a", "b", "c")            // ArraySeq[String]
unsafeWrapArray
def unsafeWrapArray[T](x: Array[T]): ArraySeq[T]

Wrap an existing array in an ArraySeq without copying. The array should not be modified after wrapping.

Usage:

val array = Array(1, 2, 3, 4, 5)
val arraySeq = ArraySeq.unsafeWrapArray(array)
empty
def empty[T <: AnyRef]: ArraySeq[T]

Create an empty ArraySeq.

Specialized ArraySeq Types

ArraySeq provides specialized implementations for primitive types to avoid boxing:

final class ofRef[T <: AnyRef](val unsafeArray: Array[T]) extends ArraySeq[T]
final class ofInt(val unsafeArray: Array[Int]) extends ArraySeq[Int]
final class ofDouble(val unsafeArray: Array[Double]) extends ArraySeq[Double]
final class ofLong(val unsafeArray: Array[Long]) extends ArraySeq[Long]
final class ofFloat(val unsafeArray: Array[Float]) extends ArraySeq[Float]
final class ofChar(val unsafeArray: Array[Char]) extends ArraySeq[Char]
final class ofByte(val unsafeArray: Array[Byte]) extends ArraySeq[Byte]
final class ofShort(val unsafeArray: Array[Short]) extends ArraySeq[Short]
final class ofBoolean(val unsafeArray: Array[Boolean]) extends ArraySeq[Boolean]
final class ofUnit(val unsafeArray: Array[Unit]) extends ArraySeq[Unit]

Usage Examples

LazyList Examples

Infinite Sequences

import scala.collection.compat.immutable._

// Fibonacci sequence
val fibs: LazyList[BigInt] = BigInt(0) #:: BigInt(1) #:: 
  fibs.zip(fibs.tail).map { case (a, b) => a + b }

fibs.take(10).foreach(println)  // Print first 10 Fibonacci numbers

// Prime numbers using sieve
def sieve(s: LazyList[Int]): LazyList[Int] = 
  s.head #:: sieve(s.tail.filter(_ % s.head != 0))

val primes = sieve(LazyList.from(2))
primes.take(10).toList  // List(2, 3, 5, 7, 11, 13, 17, 19, 23, 29)

Finite Lazy Computation

// Expensive computation with memoization
def expensiveCompute(n: Int): Int = {
  Thread.sleep(100)  // Simulate expensive operation
  n * n
}

val lazyResults = LazyList.tabulate(10)(expensiveCompute)
// Only computed when accessed
val firstFew = lazyResults.take(3).toList

ArraySeq Examples

Efficient Indexed Access

import scala.collection.compat.immutable._

val seq = ArraySeq(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

// O(1) access
val element = seq(5)  // 6
val length = seq.length  // 10

// Efficient operations
val doubled = seq.map(_ * 2)
val filtered = seq.filter(_ % 2 == 0)

Working with Primitive Arrays

// Specialized for primitives - no boxing
val intSeq = ArraySeq(1, 2, 3, 4, 5)        // ArraySeq.ofInt
val doubleSeq = ArraySeq(1.0, 2.0, 3.0)     // ArraySeq.ofDouble
val boolSeq = ArraySeq(true, false, true)   // ArraySeq.ofBoolean

// Wrap existing arrays
val array = Array(10, 20, 30, 40, 50)
val wrapped = ArraySeq.unsafeWrapArray(array)

Performance Characteristics

LazyList

  • Head access: O(1) for evaluated elements
  • Tail access: O(1) for evaluated elements
  • Memory: Elements are memoized after first evaluation
  • Evaluation: Lazy - elements computed only when needed
  • Best for: Infinite sequences, expensive computations, streaming data

ArraySeq

  • Indexed access: O(1)
  • Length: O(1)
  • Memory: Compact array storage, specialized for primitives
  • Prepend/append: O(n) - creates new array
  • Best for: Frequent random access, small memory footprint, primitive collections

Migration Notes

  • LazyList replaces Stream in Scala 2.13+
  • ArraySeq provides an immutable alternative to Vector with different performance characteristics
  • Both types are available as backports for Scala 2.11/2.12 through this compatibility library

Install with Tessl CLI

npx tessl i tessl/maven-org-scala-lang-modules--scala-collection-compat

docs

annotation-backports.md

backported-collections.md

collection-extensions.md

collection-factories.md

index.md

iterator-size-ops.md

java-interop.md

map-extensions.md

method-chaining.md

option-converters.md

resource-management.md

string-parsing.md

tile.json