The scala.deriving package provides Mirror-based system for automatic derivation of type classes and generic programming constructs.
sealed trait Mirror:
type MirroredType
type MirroredLabel <: StringBase trait for all mirrors providing the reflected type and its name as a string literal type.
trait Mirror.Product extends Mirror:
type MirroredElemTypes <: Tuple
type MirroredElemLabels <: Tuple
def fromProduct(p: Product): MirroredTypeMirror for product types (case classes, tuples) providing element types, field names, and construction from Product.
trait Mirror.Sum extends Mirror:
type MirroredElemTypes <: Tuple
type MirroredElemLabels <: Tuple
def ordinal(x: MirroredType): IntMirror for sum types (sealed hierarchies, enums) providing variant types, names, and ordinal access.
trait Mirror.Singleton extends Mirror:
type MirroredMonoType
def value: MirroredMonoTypeMirror for singleton types (objects, literal types) providing direct access to the singleton value.
import scala.deriving.*
// Define a type class
trait Show[T]:
def show(value: T): String
object Show:
// Base cases
given Show[String] = identity
given Show[Int] = _.toString
given Show[Boolean] = _.toString
// Product derivation (case classes, tuples)
given [T](using m: Mirror.ProductOf[T], s: Show[m.MirroredElemTypes]): Show[T] with
def show(value: T): String =
val productValue = Tuple.fromProductTyped(value)
val elemValues = showTuple(productValue)
s"${constValue[m.MirroredLabel]}($elemValues)"
// Sum derivation (sealed hierarchies, enums)
given [T](using m: Mirror.SumOf[T], s: Show[m.MirroredElemTypes]): Show[T] with
def show(value: T): String =
val ordinal = m.ordinal(value)
showTuple(s).productIterator.toList(ordinal).asInstanceOf[Show[T]].show(value)
// Helper for showing tuples
private def showTuple[T <: Tuple: Show](t: T): String =
t.toList.map(_.toString).mkString(", ")
// Usage with case classes
case class Person(name: String, age: Int)
case class Company(name: String, employees: Int)
val person = Person("Alice", 30)
val company = Company("TechCorp", 100)
summon[Show[Person]].show(person) // "Person(Alice, 30)"
summon[Show[Company]].show(company) // "Company(TechCorp, 100)"
// Usage with sealed hierarchies
sealed trait Animal
case class Dog(name: String) extends Animal
case class Cat(name: String) extends Animal
val animals = List(Dog("Rex"), Cat("Whiskers"))
animals.map(summon[Show[Animal]].show) // List("Dog(Rex)", "Cat(Whiskers)")import scala.deriving.*
import scala.compiletime.*
// Generic size calculation
inline def size[T](value: T): Int =
inline erasedValue[T] match
case _: Mirror.ProductOf[T] => productSize(value)
case _: Mirror.SumOf[T] => 1
case _ => 1
def productSize[T](value: T)(using m: Mirror.ProductOf[T]): Int =
val tuple = Tuple.fromProductTyped(value)
tuple.size
case class User(name: String, email: String, age: Int)
val user = User("Bob", "bob@example.com", 25)
val userSize = size(user) // 3 (number of fields)
// Generic field access
inline def getFieldNames[T]: List[String] =
inline erasedValue[T] match
case _: Mirror.ProductOf[T] =>
constValueTuple[Mirror.ProductOf[T]#MirroredElemLabels].toList.asInstanceOf[List[String]]
case _ => List.empty
val userFields = getFieldNames[User] // List("name", "email", "age")import scala.deriving.*
enum Color:
case Red, Green, Blue
object Color:
given Mirror.SumOf[Color] with
type MirroredType = Color
type MirroredLabel = "Color"
type MirroredElemTypes = (Red.type, Green.type, Blue.type)
type MirroredElemLabels = ("Red", "Green", "Blue")
def ordinal(x: Color): Int = x.ordinal
// Automatic Show derivation works
import Show.given
summon[Show[Color]].show(Color.Red) // "Red"
// Generic enum utilities
inline def enumValues[T](using m: Mirror.SumOf[T]): List[T] =
// Implementation would extract all enum values
???
inline def enumFromString[T](s: String)(using m: Mirror.SumOf[T]): Option[T] =
// Implementation would parse string to enum value
???import scala.deriving.*
import scala.compiletime.*
trait JsonEncoder[T]:
def encode(value: T): String
object JsonEncoder:
given JsonEncoder[String] = value => s""""$value""""
given JsonEncoder[Int] = _.toString
given JsonEncoder[Boolean] = _.toString
given JsonEncoder[Double] = _.toString
// Product encoding (case classes)
given [T](using m: Mirror.ProductOf[T], encoder: JsonEncoder[m.MirroredElemTypes]): JsonEncoder[T] with
def encode(value: T): String =
val labels = constValueTuple[m.MirroredElemLabels].toList.asInstanceOf[List[String]]
val values = Tuple.fromProductTyped(value)
val encodedValues = encodeProduct(values, labels)
s"{$encodedValues}"
// Sum encoding (sealed traits)
given [T](using m: Mirror.SumOf[T], encoder: JsonEncoder[m.MirroredElemTypes]): JsonEncoder[T] with
def encode(value: T): String =
val ordinal = m.ordinal(value)
val labels = constValueTuple[m.MirroredElemLabels].toList.asInstanceOf[List[String]]
val typeName = labels(ordinal)
// Delegate to product encoding for the specific case
encodeSum(value, typeName)
private def encodeProduct[T <: Tuple](values: T, labels: List[String]): String =
values.toList.zip(labels).map { case (value, label) =>
s""""$label": ${encodeValue(value)}"""
}.mkString(", ")
private def encodeSum[T](value: T, typeName: String): String =
s"""{"type": "$typeName", "value": ${encodeValue(value)}}"""
private def encodeValue(value: Any): String =
value match
case s: String => s""""$s""""
case n: Int => n.toString
case b: Boolean => b.toString
case d: Double => d.toString
case _ => "null"
// Usage
case class Address(street: String, city: String)
case class Person(name: String, age: Int, address: Address)
val person = Person("Alice", 30, Address("Main St", "Boston"))
val json = summon[JsonEncoder[Person]].encode(person)
// {"name": "Alice", "age": 30, "address": {"street": "Main St", "city": "Boston"}}import scala.deriving.*
import scala.compiletime.*
trait Validator[T]:
def validate(value: T): List[String] // List of error messages
object Validator:
given Validator[String] = _ => List.empty
given Validator[Int] = value =>
if value < 0 then List("Must be non-negative") else List.empty
// Product validation
given [T](using m: Mirror.ProductOf[T], v: Validator[m.MirroredElemTypes]): Validator[T] with
def validate(value: T): List[String] =
val tuple = Tuple.fromProductTyped(value)
val labels = constValueTuple[m.MirroredElemLabels].toList.asInstanceOf[List[String]]
validateProduct(tuple, labels)
private def validateProduct[T <: Tuple](values: T, labels: List[String]): List[String] =
values.toList.zip(labels).flatMap { case (value, label) =>
validateValue(value).map(error => s"$label: $error")
}
private def validateValue(value: Any): List[String] =
value match
case s: String => Validator[String].validate(s)
case i: Int => Validator[Int].validate(i)
case _ => List.empty
// Custom validators with annotations
case class User(
name: String, // Must be non-empty
age: Int, // Must be positive
email: String // Must contain @
) derives Validator
val user1 = User("", -5, "invalid-email")
val errors = summon[Validator[User]].validate(user1)
// List("name: Must be non-empty", "age: Must be positive", "email: Must contain @")import scala.deriving.*
trait Eq[T]:
def eql(x: T, y: T): Boolean
object Eq:
given Eq[String] = _ == _
given Eq[Int] = _ == _
given Eq[Boolean] = _ == _
// Generic product equality
given [T](using m: Mirror.ProductOf[T], eq: Eq[m.MirroredElemTypes]): Eq[T] with
def eql(x: T, y: T): Boolean =
val xTuple = Tuple.fromProductTyped(x)
val yTuple = Tuple.fromProductTyped(y)
eqlTuple(xTuple, yTuple)
// Generic sum equality
given [T](using m: Mirror.SumOf[T], eq: Eq[m.MirroredElemTypes]): Eq[T] with
def eql(x: T, y: T): Boolean =
val xOrdinal = m.ordinal(x)
val yOrdinal = m.ordinal(y)
xOrdinal == yOrdinal && eqlAtOrdinal(x, y, xOrdinal)
private def eqlTuple[T <: Tuple: Eq](x: T, y: T): Boolean =
// Implementation would compare tuple elements
x.toList.zip(y.toList).forall { case (a, b) => eqlValue(a, b) }
private def eqlAtOrdinal[T](x: T, y: T, ordinal: Int): Boolean =
// Implementation would compare at specific variant
x == y // Simplified
private def eqlValue(x: Any, y: Any): Boolean =
(x, y) match
case (s1: String, s2: String) => s1 == s2
case (i1: Int, i2: Int) => i1 == i2
case (b1: Boolean, b2: Boolean) => b1 == b2
case _ => false
// Usage
case class Point(x: Int, y: Int) derives Eq
val p1 = Point(1, 2)
val p2 = Point(1, 2)
val p3 = Point(2, 3)
summon[Eq[Point]].eql(p1, p2) // true
summon[Eq[Point]].eql(p1, p3) // falseimport scala.deriving.*
import scala.compiletime.*
// Generic transformation
trait Transform[From, To]:
def transform(from: From): To
object Transform:
// Identity transformation
given [T]: Transform[T, T] = identity
// Product to product transformation (same structure)
given [From, To](using
mFrom: Mirror.ProductOf[From],
mTo: Mirror.ProductOf[To],
trans: Transform[mFrom.MirroredElemTypes, mTo.MirroredElemTypes]
): Transform[From, To] with
def transform(from: From): To =
val fromTuple = Tuple.fromProductTyped(from)
val toTuple = transformTuple(fromTuple)
mTo.fromProduct(toTuple.asInstanceOf[Product])
private def transformTuple[From <: Tuple, To <: Tuple](from: From)(using Transform[From, To]): To =
// Implementation would transform tuple elements
???
// Generic copying with field updates
inline def copy[T, U](original: T, updates: U)(using m: Mirror.ProductOf[T]): T =
// Implementation would merge original with updates
???
case class PersonV1(name: String, age: Int)
case class PersonV2(name: String, age: Int, email: String)
// Transform between versions
given Transform[PersonV1, PersonV2] with
def transform(p1: PersonV1): PersonV2 =
PersonV2(p1.name, p1.age, "")
val p1 = PersonV1("Alice", 30)
val p2 = summon[Transform[PersonV1, PersonV2]].transform(p1)
// PersonV2("Alice", 30, "")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.