0
# Tuples and Product Types
1
2
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.
3
4
## Core Tuple API
5
6
### Tuple Trait
7
8
```scala { .api }
9
sealed trait Tuple extends Product:
10
def toArray: Array[Object]
11
def toList: List[Union[this.type]]
12
def toIArray: IArray[Object]
13
def :*[L](x: L): This :* L
14
def *:[H](x: H): H *: This
15
def apply(n: Int): Elem[This, n.type]
16
def head: Head[This]
17
def tail: Tail[This]
18
def init: Init[This]
19
def last: Last[This]
20
def ++[T2 <: Tuple](that: Tuple): This ++ that.type
21
def size: Size[This]
22
def zip[T2 <: Tuple](t2: T2): Zip[This, T2]
23
def map[F[_]](f: [t] => t => F[t]): Map[this.type, F]
24
def take(n: Int): Take[This, n.type]
25
def drop(n: Int): Drop[This, n.type]
26
def splitAt(n: Int): Split[This, n.type]
27
def reverse: Reverse[This]
28
```
29
30
Base trait for all tuples providing rich manipulation operations.
31
32
### Tuple Construction Types
33
34
```scala { .api }
35
type EmptyTuple = EmptyTuple.type
36
case object EmptyTuple extends Tuple
37
38
sealed trait NonEmptyTuple extends Tuple
39
sealed abstract class *:[+H, +T <: Tuple] extends NonEmptyTuple
40
```
41
42
- **`EmptyTuple`**: The empty tuple `()`
43
- **`NonEmptyTuple`**: Base trait for non-empty tuples
44
- **`*:`**: Infix type constructor for building tuples (e.g., `String *: Int *: EmptyTuple`)
45
46
### Tuple Factory Methods
47
48
```scala { .api }
49
object Tuple:
50
def apply(): EmptyTuple
51
def apply[T](x: T): T *: EmptyTuple
52
def fromArray[T](xs: Array[T]): Tuple
53
def fromIArray[T](xs: IArray[T]): Tuple
54
def fromProduct(product: Product): Tuple
55
def fromProductTyped[P <: Product](p: P)(using m: Mirror.ProductOf[P]): m.MirroredElemTypes
56
```
57
58
Factory methods for creating tuples from various sources.
59
60
## Type-Level Operations
61
62
### Element Access Types
63
64
```scala { .api }
65
type Head[X <: Tuple] // First element type
66
type Tail[X <: Tuple] <: Tuple // All but first element
67
type Init[X <: Tuple] <: Tuple // All but last element
68
type Last[X <: Tuple] // Last element type
69
type Elem[X <: Tuple, N <: Int] // Element at index N
70
```
71
72
### Tuple Manipulation Types
73
74
```scala { .api }
75
type Append[X <: Tuple, Y] <: NonEmptyTuple
76
type :*[X <: Tuple, Y] = Append[X, Y] // Infix append
77
78
type Concat[X <: Tuple, +Y <: Tuple] <: Tuple
79
type ++[X <: Tuple, +Y <: Tuple] = Concat[X, Y] // Infix concat
80
81
type Size[X <: Tuple] <: Int
82
type Reverse[X <: Tuple] <: Tuple
83
84
type Take[T <: Tuple, N <: Int] <: Tuple
85
type Drop[T <: Tuple, N <: Int] <: Tuple
86
type Split[T <: Tuple, N <: Int] = (Take[T, N], Drop[T, N])
87
```
88
89
### Advanced Type Operations
90
91
```scala { .api }
92
type Map[Tup <: Tuple, F[_ <: Union[Tup]]] <: Tuple
93
type FlatMap[Tup <: Tuple, F[_ <: Union[Tup]] <: Tuple] <: Tuple
94
type Filter[Tup <: Tuple, P[_ <: Union[Tup]] <: Boolean] <: Tuple
95
type Zip[T1 <: Tuple, T2 <: Tuple] <: Tuple
96
type Union[T <: Tuple] // Union of all element types
97
type Fold[Tup <: Tuple, Z, F[_, _]]
98
```
99
100
## Named Tuples
101
102
### NamedTuple Type
103
104
```scala { .api }
105
opaque type NamedTuple[N <: Tuple, +V <: Tuple] >: V <: AnyNamedTuple = V
106
opaque type AnyNamedTuple = Any
107
type Empty = NamedTuple[EmptyTuple, EmptyTuple]
108
```
109
110
Named tuples provide field names as part of the type, enabling type-safe field access.
111
112
### NamedTuple Factory Methods
113
114
```scala { .api }
115
object NamedTuple:
116
def apply[N <: Tuple, V <: Tuple](x: V): NamedTuple[N, V]
117
def build[N <: Tuple]()[V <: Tuple](x: V): NamedTuple[N, V]
118
val Empty: Empty
119
```
120
121
### NamedTuple Operations
122
123
```scala { .api }
124
extension [N <: Tuple, V <: Tuple](x: NamedTuple[N, V]):
125
def toTuple: V
126
def apply(n: Int): Elem[NamedTuple[N, V], n.type]
127
def size: Size[NamedTuple[N, V]]
128
def head: Head[NamedTuple[N, V]]
129
def tail: Tail[NamedTuple[N, V]]
130
def last: Last[NamedTuple[N, V]]
131
def init: Init[NamedTuple[N, V]]
132
def take(n: Int): Take[NamedTuple[N, V], n.type]
133
def drop(n: Int): Drop[NamedTuple[N, V], n.type]
134
def ++[N2, V2](that: NamedTuple[N2, V2])(using Tuple.Disjoint[N, N2] =:= true): Concat[...]
135
def map[F[_]](f: [t] => t => F[t]): Map[NamedTuple[N, V], F]
136
def reverse: Reverse[NamedTuple[N, V]]
137
def zip[V2](that: NamedTuple[N, V2]): Zip[NamedTuple[N, V], NamedTuple[N, V2]]
138
```
139
140
### NamedTuple Type Operations
141
142
```scala { .api }
143
type Names[X <: AnyNamedTuple] <: Tuple // Extract field names
144
type DropNames[NT <: AnyNamedTuple] <: Tuple // Extract values
145
type From[T] <: AnyNamedTuple // Convert class fields to named tuple
146
```
147
148
## Usage Examples
149
150
### Basic Tuple Operations
151
152
```scala
153
// Creating tuples
154
val empty = EmptyTuple
155
val single = Tuple("hello")
156
val triple = ("Alice", 25, true)
157
158
// Accessing elements
159
val first = triple.head // "Alice"
160
val rest = triple.tail // (25, true)
161
val last = triple.last // true
162
val second = triple.apply(1) // 25
163
164
// Adding elements
165
val extended = triple :* "Engineer" // ("Alice", 25, true, "Engineer")
166
val prepended = "Dr." *: triple // ("Dr.", "Alice", 25, true)
167
168
// Tuple operations
169
val size = triple.size // 3
170
val reversed = triple.reverse // (true, 25, "Alice")
171
val firstTwo = triple.take(2) // ("Alice", 25)
172
val lastTwo = triple.drop(1) // (25, true)
173
```
174
175
### Type-Level Computations
176
177
```scala
178
import scala.compiletime.constValue
179
180
type MyTuple = String *: Int *: Boolean *: EmptyTuple
181
val size: 3 = constValue[Tuple.Size[MyTuple]]
182
type FirstElement = Tuple.Head[MyTuple] // String
183
type RestElements = Tuple.Tail[MyTuple] // Int *: Boolean *: EmptyTuple
184
```
185
186
### Named Tuples
187
188
```scala
189
// Creating named tuples with literal syntax
190
val person = (name = "Bob", age = 30, active = true)
191
192
// Type-safe field access
193
val name: String = person.name
194
val age: Int = person.age
195
196
// Operations preserve names
197
val reversed = person.reverse // (active = true, age = 30, name = "Bob")
198
val older = person.map([t] => (x: t) => x match
199
case age: Int => age + 1
200
case other => other
201
) // (name = "Bob", age = 31, active = true)
202
203
// Concatenation requires disjoint names
204
val address = (street = "Main St", city = "Boston")
205
val fullInfo = person ++ address
206
// (name = "Bob", age = 30, active = true, street = "Main St", city = "Boston")
207
```
208
209
### Generic Programming with Tuples
210
211
```scala
212
def processTuple[T <: Tuple](t: T): String =
213
t.toList.mkString(", ")
214
215
def tupleMap[T <: Tuple, F[_]](t: T)(f: [U] => U => F[U]): Tuple.Map[T, F] =
216
t.map(f)
217
218
// Usage
219
val data = ("hello", 42, true)
220
processTuple(data) // "hello, 42, true"
221
222
val lengths = tupleMap(("abc", "hello", "x"))([T] => (s: T) => s match
223
case str: String => str.length
224
case other => 0
225
) // (3, 5, 1)
226
```
227
228
### Pattern Matching
229
230
```scala
231
def analyzeTuple(t: Tuple): String = t match
232
case EmptyTuple => "empty"
233
case single *: EmptyTuple => s"single: $single"
234
case first *: second *: EmptyTuple => s"pair: $first, $second"
235
case head *: tail => s"multiple: $head and ${tail.size} more"
236
237
// With named tuples
238
def analyzeUser(user: AnyNamedTuple): String = user match
239
case person if person.toTuple.size == 3 => "Person with 3 fields"
240
case _ => "Other structure"
241
```
242
243
### Conversion Operations
244
245
```scala
246
val tuple = ("a", "b", "c")
247
248
// Convert to collections
249
val array = tuple.toArray // Array("a", "b", "c")
250
val list = tuple.toList // List("a", "b", "c")
251
val iarray = tuple.toIArray // IArray("a", "b", "c")
252
253
// From collections
254
val fromArray = Tuple.fromArray(Array(1, 2, 3))
255
val fromProduct = Tuple.fromProduct(("x", "y"))
256
```
257
258
### Advanced Type-Level Operations
259
260
```scala
261
// Filter tuple elements by type predicate
262
type IsString[X] <: Boolean = X match
263
case String => true
264
case _ => false
265
266
type MixedTuple = Int *: String *: Boolean *: String *: EmptyTuple
267
type StringsOnly = Tuple.Filter[MixedTuple, IsString] // String *: String *: EmptyTuple
268
269
// Map over tuple types
270
type ToOption[X] = Option[X]
271
type OptionTuple = Tuple.Map[MixedTuple, ToOption]
272
// Option[Int] *: Option[String] *: Option[Boolean] *: Option[String] *: EmptyTuple
273
274
// Fold tuple types
275
type StringConcat[X, Y] = (X, Y) match
276
case (String, String) => String
277
case _ => String
278
279
type AllStrings = String *: String *: String *: EmptyTuple
280
type ConcatResult = Tuple.Fold[AllStrings, String, StringConcat] // String
281
```
282
283
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.