or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

annotations.mdcompile-time.mdgeneric-programming.mdimmutable-arrays.mdindex.mdmacros.mdstructural-types.mdtuples.mdtype-safe-equality.md
tile.json

tuples.mddocs/

Tuples and Product Types

Scala 3 provides a rich tuple system supporting arbitrary arity, comprehensive type-level operations, and both traditional and named tuples for enhanced type safety and expressiveness.

Core Tuple API

Tuple Trait

sealed trait Tuple extends Product:
  def toArray: Array[Object]
  def toList: List[Union[this.type]]
  def toIArray: IArray[Object]
  def :*[L](x: L): This :* L
  def *:[H](x: H): H *: This
  def apply(n: Int): Elem[This, n.type]
  def head: Head[This]
  def tail: Tail[This]
  def init: Init[This]
  def last: Last[This]
  def ++[T2 <: Tuple](that: Tuple): This ++ that.type
  def size: Size[This]
  def zip[T2 <: Tuple](t2: T2): Zip[This, T2]
  def map[F[_]](f: [t] => t => F[t]): Map[this.type, F]
  def take(n: Int): Take[This, n.type]
  def drop(n: Int): Drop[This, n.type]
  def splitAt(n: Int): Split[This, n.type]
  def reverse: Reverse[This]

Base trait for all tuples providing rich manipulation operations.

Tuple Construction Types

type EmptyTuple = EmptyTuple.type
case object EmptyTuple extends Tuple

sealed trait NonEmptyTuple extends Tuple
sealed abstract class *:[+H, +T <: Tuple] extends NonEmptyTuple
  • EmptyTuple: The empty tuple ()
  • NonEmptyTuple: Base trait for non-empty tuples
  • *:: Infix type constructor for building tuples (e.g., String *: Int *: EmptyTuple)

Tuple Factory Methods

object Tuple:
  def apply(): EmptyTuple
  def apply[T](x: T): T *: EmptyTuple
  def fromArray[T](xs: Array[T]): Tuple
  def fromIArray[T](xs: IArray[T]): Tuple
  def fromProduct(product: Product): Tuple
  def fromProductTyped[P <: Product](p: P)(using m: Mirror.ProductOf[P]): m.MirroredElemTypes

Factory methods for creating tuples from various sources.

Type-Level Operations

Element Access Types

type Head[X <: Tuple]  // First element type
type Tail[X <: Tuple] <: Tuple  // All but first element
type Init[X <: Tuple] <: Tuple  // All but last element  
type Last[X <: Tuple]  // Last element type
type Elem[X <: Tuple, N <: Int]  // Element at index N

Tuple Manipulation Types

type Append[X <: Tuple, Y] <: NonEmptyTuple
type :*[X <: Tuple, Y] = Append[X, Y]  // Infix append

type Concat[X <: Tuple, +Y <: Tuple] <: Tuple
type ++[X <: Tuple, +Y <: Tuple] = Concat[X, Y]  // Infix concat

type Size[X <: Tuple] <: Int
type Reverse[X <: Tuple] <: Tuple

type Take[T <: Tuple, N <: Int] <: Tuple
type Drop[T <: Tuple, N <: Int] <: Tuple
type Split[T <: Tuple, N <: Int] = (Take[T, N], Drop[T, N])

Advanced Type Operations

type Map[Tup <: Tuple, F[_ <: Union[Tup]]] <: Tuple
type FlatMap[Tup <: Tuple, F[_ <: Union[Tup]] <: Tuple] <: Tuple
type Filter[Tup <: Tuple, P[_ <: Union[Tup]] <: Boolean] <: Tuple
type Zip[T1 <: Tuple, T2 <: Tuple] <: Tuple
type Union[T <: Tuple]  // Union of all element types
type Fold[Tup <: Tuple, Z, F[_, _]]

Named Tuples

NamedTuple Type

opaque type NamedTuple[N <: Tuple, +V <: Tuple] >: V <: AnyNamedTuple = V
opaque type AnyNamedTuple = Any
type Empty = NamedTuple[EmptyTuple, EmptyTuple]

Named tuples provide field names as part of the type, enabling type-safe field access.

NamedTuple Factory Methods

object NamedTuple:
  def apply[N <: Tuple, V <: Tuple](x: V): NamedTuple[N, V]
  def build[N <: Tuple]()[V <: Tuple](x: V): NamedTuple[N, V]
  val Empty: Empty

NamedTuple Operations

extension [N <: Tuple, V <: Tuple](x: NamedTuple[N, V]):
  def toTuple: V
  def apply(n: Int): Elem[NamedTuple[N, V], n.type]
  def size: Size[NamedTuple[N, V]]
  def head: Head[NamedTuple[N, V]]
  def tail: Tail[NamedTuple[N, V]]
  def last: Last[NamedTuple[N, V]]
  def init: Init[NamedTuple[N, V]]
  def take(n: Int): Take[NamedTuple[N, V], n.type]
  def drop(n: Int): Drop[NamedTuple[N, V], n.type]
  def ++[N2, V2](that: NamedTuple[N2, V2])(using Tuple.Disjoint[N, N2] =:= true): Concat[...]
  def map[F[_]](f: [t] => t => F[t]): Map[NamedTuple[N, V], F]
  def reverse: Reverse[NamedTuple[N, V]]
  def zip[V2](that: NamedTuple[N, V2]): Zip[NamedTuple[N, V], NamedTuple[N, V2]]

NamedTuple Type Operations

type Names[X <: AnyNamedTuple] <: Tuple  // Extract field names
type DropNames[NT <: AnyNamedTuple] <: Tuple  // Extract values
type From[T] <: AnyNamedTuple  // Convert class fields to named tuple

Usage Examples

Basic Tuple Operations

// Creating tuples
val empty = EmptyTuple
val single = Tuple("hello")
val triple = ("Alice", 25, true)

// Accessing elements
val first = triple.head        // "Alice"
val rest = triple.tail         // (25, true)
val last = triple.last         // true
val second = triple.apply(1)   // 25

// Adding elements
val extended = triple :* "Engineer"    // ("Alice", 25, true, "Engineer")
val prepended = "Dr." *: triple        // ("Dr.", "Alice", 25, true)

// Tuple operations
val size = triple.size                 // 3
val reversed = triple.reverse          // (true, 25, "Alice")
val firstTwo = triple.take(2)          // ("Alice", 25)
val lastTwo = triple.drop(1)           // (25, true)

Type-Level Computations

import scala.compiletime.constValue

type MyTuple = String *: Int *: Boolean *: EmptyTuple
val size: 3 = constValue[Tuple.Size[MyTuple]]
type FirstElement = Tuple.Head[MyTuple]  // String
type RestElements = Tuple.Tail[MyTuple]  // Int *: Boolean *: EmptyTuple

Named Tuples

// Creating named tuples with literal syntax
val person = (name = "Bob", age = 30, active = true)

// Type-safe field access
val name: String = person.name
val age: Int = person.age

// Operations preserve names
val reversed = person.reverse  // (active = true, age = 30, name = "Bob")
val older = person.map([t] => (x: t) => x match
  case age: Int => age + 1
  case other => other
)  // (name = "Bob", age = 31, active = true)

// Concatenation requires disjoint names
val address = (street = "Main St", city = "Boston")
val fullInfo = person ++ address
// (name = "Bob", age = 30, active = true, street = "Main St", city = "Boston")

Generic Programming with Tuples

def processTuple[T <: Tuple](t: T): String =
  t.toList.mkString(", ")

def tupleMap[T <: Tuple, F[_]](t: T)(f: [U] => U => F[U]): Tuple.Map[T, F] =
  t.map(f)

// Usage
val data = ("hello", 42, true)
processTuple(data)  // "hello, 42, true"

val lengths = tupleMap(("abc", "hello", "x"))([T] => (s: T) => s match
  case str: String => str.length
  case other => 0
)  // (3, 5, 1)

Pattern Matching

def analyzeTuple(t: Tuple): String = t match
  case EmptyTuple => "empty"
  case single *: EmptyTuple => s"single: $single"
  case first *: second *: EmptyTuple => s"pair: $first, $second"
  case head *: tail => s"multiple: $head and ${tail.size} more"

// With named tuples
def analyzeUser(user: AnyNamedTuple): String = user match
  case person if person.toTuple.size == 3 => "Person with 3 fields"
  case _ => "Other structure"

Conversion Operations

val tuple = ("a", "b", "c")

// Convert to collections
val array = tuple.toArray        // Array("a", "b", "c")
val list = tuple.toList          // List("a", "b", "c")
val iarray = tuple.toIArray      // IArray("a", "b", "c")

// From collections
val fromArray = Tuple.fromArray(Array(1, 2, 3))
val fromProduct = Tuple.fromProduct(("x", "y"))

Advanced Type-Level Operations

// Filter tuple elements by type predicate
type IsString[X] <: Boolean = X match
  case String => true
  case _ => false

type MixedTuple = Int *: String *: Boolean *: String *: EmptyTuple
type StringsOnly = Tuple.Filter[MixedTuple, IsString]  // String *: String *: EmptyTuple

// Map over tuple types
type ToOption[X] = Option[X]
type OptionTuple = Tuple.Map[MixedTuple, ToOption]
// Option[Int] *: Option[String] *: Option[Boolean] *: Option[String] *: EmptyTuple

// Fold tuple types
type StringConcat[X, Y] = (X, Y) match
  case (String, String) => String
  case _ => String

type AllStrings = String *: String *: String *: EmptyTuple
type ConcatResult = Tuple.Fold[AllStrings, String, StringConcat]  // String

This comprehensive tuple system enables both runtime manipulation and compile-time type-level programming, making Scala 3 tuples extremely powerful for generic programming and type-safe data manipulation.