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

generic-programming.mddocs/

Generic Programming and Derivation

The scala.deriving package provides Mirror-based system for automatic derivation of type classes and generic programming constructs.

Core Mirror Types

Base Mirror

sealed trait Mirror:
  type MirroredType
  type MirroredLabel <: String

Base trait for all mirrors providing the reflected type and its name as a string literal type.

Product Mirror

trait Mirror.Product extends Mirror:
  type MirroredElemTypes <: Tuple
  type MirroredElemLabels <: Tuple
  def fromProduct(p: Product): MirroredType

Mirror for product types (case classes, tuples) providing element types, field names, and construction from Product.

Sum Mirror

trait Mirror.Sum extends Mirror:
  type MirroredElemTypes <: Tuple
  type MirroredElemLabels <: Tuple
  def ordinal(x: MirroredType): Int

Mirror for sum types (sealed hierarchies, enums) providing variant types, names, and ordinal access.

Singleton Mirror

trait Mirror.Singleton extends Mirror:
  type MirroredMonoType
  def value: MirroredMonoType

Mirror for singleton types (objects, literal types) providing direct access to the singleton value.

Usage Examples

Automatic Type Class Derivation

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)")

Generic Programming with Mirrors

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")

Enum Derivation

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
  ???

JSON Serialization Example

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"}}

Validation Framework

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 @")

Generic Comparison

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)  // false

Advanced Generic Programming

import 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.