or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

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

generic-programming.mddocs/

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.