0
# Generic Programming and Derivation
1
2
The `scala.deriving` package provides Mirror-based system for automatic derivation of type classes and generic programming constructs.
3
4
## Core Mirror Types
5
6
### Base Mirror
7
8
```scala { .api }
9
sealed trait Mirror:
10
type MirroredType
11
type MirroredLabel <: String
12
```
13
14
Base trait for all mirrors providing the reflected type and its name as a string literal type.
15
16
### Product Mirror
17
18
```scala { .api }
19
trait Mirror.Product extends Mirror:
20
type MirroredElemTypes <: Tuple
21
type MirroredElemLabels <: Tuple
22
def fromProduct(p: Product): MirroredType
23
```
24
25
Mirror for product types (case classes, tuples) providing element types, field names, and construction from Product.
26
27
### Sum Mirror
28
29
```scala { .api }
30
trait Mirror.Sum extends Mirror:
31
type MirroredElemTypes <: Tuple
32
type MirroredElemLabels <: Tuple
33
def ordinal(x: MirroredType): Int
34
```
35
36
Mirror for sum types (sealed hierarchies, enums) providing variant types, names, and ordinal access.
37
38
### Singleton Mirror
39
40
```scala { .api }
41
trait Mirror.Singleton extends Mirror:
42
type MirroredMonoType
43
def value: MirroredMonoType
44
```
45
46
Mirror for singleton types (objects, literal types) providing direct access to the singleton value.
47
48
## Usage Examples
49
50
### Automatic Type Class Derivation
51
52
```scala
53
import scala.deriving.*
54
55
// Define a type class
56
trait Show[T]:
57
def show(value: T): String
58
59
object Show:
60
// Base cases
61
given Show[String] = identity
62
given Show[Int] = _.toString
63
given Show[Boolean] = _.toString
64
65
// Product derivation (case classes, tuples)
66
given [T](using m: Mirror.ProductOf[T], s: Show[m.MirroredElemTypes]): Show[T] with
67
def show(value: T): String =
68
val productValue = Tuple.fromProductTyped(value)
69
val elemValues = showTuple(productValue)
70
s"${constValue[m.MirroredLabel]}($elemValues)"
71
72
// Sum derivation (sealed hierarchies, enums)
73
given [T](using m: Mirror.SumOf[T], s: Show[m.MirroredElemTypes]): Show[T] with
74
def show(value: T): String =
75
val ordinal = m.ordinal(value)
76
showTuple(s).productIterator.toList(ordinal).asInstanceOf[Show[T]].show(value)
77
78
// Helper for showing tuples
79
private def showTuple[T <: Tuple: Show](t: T): String =
80
t.toList.map(_.toString).mkString(", ")
81
82
// Usage with case classes
83
case class Person(name: String, age: Int)
84
case class Company(name: String, employees: Int)
85
86
val person = Person("Alice", 30)
87
val company = Company("TechCorp", 100)
88
89
summon[Show[Person]].show(person) // "Person(Alice, 30)"
90
summon[Show[Company]].show(company) // "Company(TechCorp, 100)"
91
92
// Usage with sealed hierarchies
93
sealed trait Animal
94
case class Dog(name: String) extends Animal
95
case class Cat(name: String) extends Animal
96
97
val animals = List(Dog("Rex"), Cat("Whiskers"))
98
animals.map(summon[Show[Animal]].show) // List("Dog(Rex)", "Cat(Whiskers)")
99
```
100
101
### Generic Programming with Mirrors
102
103
```scala
104
import scala.deriving.*
105
import scala.compiletime.*
106
107
// Generic size calculation
108
inline def size[T](value: T): Int =
109
inline erasedValue[T] match
110
case _: Mirror.ProductOf[T] => productSize(value)
111
case _: Mirror.SumOf[T] => 1
112
case _ => 1
113
114
def productSize[T](value: T)(using m: Mirror.ProductOf[T]): Int =
115
val tuple = Tuple.fromProductTyped(value)
116
tuple.size
117
118
case class User(name: String, email: String, age: Int)
119
val user = User("Bob", "bob@example.com", 25)
120
val userSize = size(user) // 3 (number of fields)
121
122
// Generic field access
123
inline def getFieldNames[T]: List[String] =
124
inline erasedValue[T] match
125
case _: Mirror.ProductOf[T] =>
126
constValueTuple[Mirror.ProductOf[T]#MirroredElemLabels].toList.asInstanceOf[List[String]]
127
case _ => List.empty
128
129
val userFields = getFieldNames[User] // List("name", "email", "age")
130
```
131
132
### Enum Derivation
133
134
```scala
135
import scala.deriving.*
136
137
enum Color:
138
case Red, Green, Blue
139
140
object Color:
141
given Mirror.SumOf[Color] with
142
type MirroredType = Color
143
type MirroredLabel = "Color"
144
type MirroredElemTypes = (Red.type, Green.type, Blue.type)
145
type MirroredElemLabels = ("Red", "Green", "Blue")
146
def ordinal(x: Color): Int = x.ordinal
147
148
// Automatic Show derivation works
149
import Show.given
150
summon[Show[Color]].show(Color.Red) // "Red"
151
152
// Generic enum utilities
153
inline def enumValues[T](using m: Mirror.SumOf[T]): List[T] =
154
// Implementation would extract all enum values
155
???
156
157
inline def enumFromString[T](s: String)(using m: Mirror.SumOf[T]): Option[T] =
158
// Implementation would parse string to enum value
159
???
160
```
161
162
### JSON Serialization Example
163
164
```scala
165
import scala.deriving.*
166
import scala.compiletime.*
167
168
trait JsonEncoder[T]:
169
def encode(value: T): String
170
171
object JsonEncoder:
172
given JsonEncoder[String] = value => s""""$value""""
173
given JsonEncoder[Int] = _.toString
174
given JsonEncoder[Boolean] = _.toString
175
given JsonEncoder[Double] = _.toString
176
177
// Product encoding (case classes)
178
given [T](using m: Mirror.ProductOf[T], encoder: JsonEncoder[m.MirroredElemTypes]): JsonEncoder[T] with
179
def encode(value: T): String =
180
val labels = constValueTuple[m.MirroredElemLabels].toList.asInstanceOf[List[String]]
181
val values = Tuple.fromProductTyped(value)
182
val encodedValues = encodeProduct(values, labels)
183
s"{$encodedValues}"
184
185
// Sum encoding (sealed traits)
186
given [T](using m: Mirror.SumOf[T], encoder: JsonEncoder[m.MirroredElemTypes]): JsonEncoder[T] with
187
def encode(value: T): String =
188
val ordinal = m.ordinal(value)
189
val labels = constValueTuple[m.MirroredElemLabels].toList.asInstanceOf[List[String]]
190
val typeName = labels(ordinal)
191
// Delegate to product encoding for the specific case
192
encodeSum(value, typeName)
193
194
private def encodeProduct[T <: Tuple](values: T, labels: List[String]): String =
195
values.toList.zip(labels).map { case (value, label) =>
196
s""""$label": ${encodeValue(value)}"""
197
}.mkString(", ")
198
199
private def encodeSum[T](value: T, typeName: String): String =
200
s"""{"type": "$typeName", "value": ${encodeValue(value)}}"""
201
202
private def encodeValue(value: Any): String =
203
value match
204
case s: String => s""""$s""""
205
case n: Int => n.toString
206
case b: Boolean => b.toString
207
case d: Double => d.toString
208
case _ => "null"
209
210
// Usage
211
case class Address(street: String, city: String)
212
case class Person(name: String, age: Int, address: Address)
213
214
val person = Person("Alice", 30, Address("Main St", "Boston"))
215
val json = summon[JsonEncoder[Person]].encode(person)
216
// {"name": "Alice", "age": 30, "address": {"street": "Main St", "city": "Boston"}}
217
```
218
219
### Validation Framework
220
221
```scala
222
import scala.deriving.*
223
import scala.compiletime.*
224
225
trait Validator[T]:
226
def validate(value: T): List[String] // List of error messages
227
228
object Validator:
229
given Validator[String] = _ => List.empty
230
given Validator[Int] = value =>
231
if value < 0 then List("Must be non-negative") else List.empty
232
233
// Product validation
234
given [T](using m: Mirror.ProductOf[T], v: Validator[m.MirroredElemTypes]): Validator[T] with
235
def validate(value: T): List[String] =
236
val tuple = Tuple.fromProductTyped(value)
237
val labels = constValueTuple[m.MirroredElemLabels].toList.asInstanceOf[List[String]]
238
validateProduct(tuple, labels)
239
240
private def validateProduct[T <: Tuple](values: T, labels: List[String]): List[String] =
241
values.toList.zip(labels).flatMap { case (value, label) =>
242
validateValue(value).map(error => s"$label: $error")
243
}
244
245
private def validateValue(value: Any): List[String] =
246
value match
247
case s: String => Validator[String].validate(s)
248
case i: Int => Validator[Int].validate(i)
249
case _ => List.empty
250
251
// Custom validators with annotations
252
case class User(
253
name: String, // Must be non-empty
254
age: Int, // Must be positive
255
email: String // Must contain @
256
) derives Validator
257
258
val user1 = User("", -5, "invalid-email")
259
val errors = summon[Validator[User]].validate(user1)
260
// List("name: Must be non-empty", "age: Must be positive", "email: Must contain @")
261
```
262
263
### Generic Comparison
264
265
```scala
266
import scala.deriving.*
267
268
trait Eq[T]:
269
def eql(x: T, y: T): Boolean
270
271
object Eq:
272
given Eq[String] = _ == _
273
given Eq[Int] = _ == _
274
given Eq[Boolean] = _ == _
275
276
// Generic product equality
277
given [T](using m: Mirror.ProductOf[T], eq: Eq[m.MirroredElemTypes]): Eq[T] with
278
def eql(x: T, y: T): Boolean =
279
val xTuple = Tuple.fromProductTyped(x)
280
val yTuple = Tuple.fromProductTyped(y)
281
eqlTuple(xTuple, yTuple)
282
283
// Generic sum equality
284
given [T](using m: Mirror.SumOf[T], eq: Eq[m.MirroredElemTypes]): Eq[T] with
285
def eql(x: T, y: T): Boolean =
286
val xOrdinal = m.ordinal(x)
287
val yOrdinal = m.ordinal(y)
288
xOrdinal == yOrdinal && eqlAtOrdinal(x, y, xOrdinal)
289
290
private def eqlTuple[T <: Tuple: Eq](x: T, y: T): Boolean =
291
// Implementation would compare tuple elements
292
x.toList.zip(y.toList).forall { case (a, b) => eqlValue(a, b) }
293
294
private def eqlAtOrdinal[T](x: T, y: T, ordinal: Int): Boolean =
295
// Implementation would compare at specific variant
296
x == y // Simplified
297
298
private def eqlValue(x: Any, y: Any): Boolean =
299
(x, y) match
300
case (s1: String, s2: String) => s1 == s2
301
case (i1: Int, i2: Int) => i1 == i2
302
case (b1: Boolean, b2: Boolean) => b1 == b2
303
case _ => false
304
305
// Usage
306
case class Point(x: Int, y: Int) derives Eq
307
val p1 = Point(1, 2)
308
val p2 = Point(1, 2)
309
val p3 = Point(2, 3)
310
311
summon[Eq[Point]].eql(p1, p2) // true
312
summon[Eq[Point]].eql(p1, p3) // false
313
```
314
315
### Advanced Generic Programming
316
317
```scala
318
import scala.deriving.*
319
import scala.compiletime.*
320
321
// Generic transformation
322
trait Transform[From, To]:
323
def transform(from: From): To
324
325
object Transform:
326
// Identity transformation
327
given [T]: Transform[T, T] = identity
328
329
// Product to product transformation (same structure)
330
given [From, To](using
331
mFrom: Mirror.ProductOf[From],
332
mTo: Mirror.ProductOf[To],
333
trans: Transform[mFrom.MirroredElemTypes, mTo.MirroredElemTypes]
334
): Transform[From, To] with
335
def transform(from: From): To =
336
val fromTuple = Tuple.fromProductTyped(from)
337
val toTuple = transformTuple(fromTuple)
338
mTo.fromProduct(toTuple.asInstanceOf[Product])
339
340
private def transformTuple[From <: Tuple, To <: Tuple](from: From)(using Transform[From, To]): To =
341
// Implementation would transform tuple elements
342
???
343
344
// Generic copying with field updates
345
inline def copy[T, U](original: T, updates: U)(using m: Mirror.ProductOf[T]): T =
346
// Implementation would merge original with updates
347
???
348
349
case class PersonV1(name: String, age: Int)
350
case class PersonV2(name: String, age: Int, email: String)
351
352
// Transform between versions
353
given Transform[PersonV1, PersonV2] with
354
def transform(p1: PersonV1): PersonV2 =
355
PersonV2(p1.name, p1.age, "")
356
357
val p1 = PersonV1("Alice", 30)
358
val p2 = summon[Transform[PersonV1, PersonV2]].transform(p1)
359
// PersonV2("Alice", 30, "")
360
```
361
362
The Mirror-based derivation system in Scala 3 provides powerful capabilities for generic programming, automatic type class derivation, and compile-time reflection while maintaining type safety and performance.