scalaadvanced

Type-Level Programming with Tuples

Work with heterogeneous collections: named tuples, type-safe access, and compile-time operations.

scala
import scala.compiletime.*
import scala.deriving.Mirror

// Scala 3 named tuples / labeled types
type Person = (name: String, age: Int, email: String)

// Type-safe heterogeneous list using tuples
val record: Person = (name = "Alice", age = 30, email = "alice@test.com")

// Generic programming with Mirrors
inline def fieldNames[T](using m: Mirror.ProductOf[T]): List[String] =
  fieldNamesHelper[m.MirroredElemLabels]

inline def fieldNamesHelper[T <: Tuple]: List[String] =
  inline erasedValue[T] match
    case _: EmptyTuple => Nil
    case _: (head *: tail) =>
      constValue[head].toString :: fieldNamesHelper[tail]

// Product to Map
def toMap(product: Product): Map[String, Any] =
  product.productElementNames.zip(product.productIterator).toMap

// Case class derivation
case class User(name: String, age: Int, active: Boolean)
case class Config(host: String, port: Int, debug: Boolean, maxRetries: Int)

// Type-safe builder pattern
sealed trait HasName
sealed trait HasAge
sealed trait HasEmail

class PersonBuilder[State]:
  private var _name: String = ""
  private var _age: Int = 0
  private var _email: String = ""

  def name(n: String): PersonBuilder[State & HasName] =
    _name = n
    this.asInstanceOf[PersonBuilder[State & HasName]]

  def age(a: Int): PersonBuilder[State & HasAge] =
    _age = a
    this.asInstanceOf[PersonBuilder[State & HasAge]]

  def email(e: String): PersonBuilder[State & HasEmail] =
    _email = e
    this.asInstanceOf[PersonBuilder[State & HasEmail]]

  // Only available when all fields are set
  def build(using ev: State =:= (HasName & HasAge & HasEmail)): User =
    User(_name, _age, _email == "active")

// Tuple operations
def tupleOps(): Unit =
  val t1 = (1, "hello", true)
  val t2 = (3.14, 'c')

  // Concatenation
  val combined = t1 ++ t2
  println(s"Combined: $combined")

  // Map
  val mapped = t1.map[[X] =>> String] {
    [T] => (t: T) => t.toString
  }
  println(s"Mapped: $mapped")

  // Size
  println(s"Size: ${t1.size}")

  // toList (if homogeneous after map)
  val list = t1.toList
  println(s"ToList: $list")

  // Zip
  val names = ("Alice", "Bob", "Carol")
  val ages = (30, 25, 35)
  val zipped = names.zip(ages)
  println(s"Zipped: $zipped")

@main def run(): Unit =
  // Named tuple access
  println(s"Name: ${record.name}")
  println(s"Age: ${record.age}")
  println(s"Email: ${record.email}")

  // Field names via Mirror
  println(s"\nUser fields: ${fieldNames[User]}")
  println(s"Config fields: ${fieldNames[Config]}")

  // Product to map
  val user = User("Bob", 25, true)
  println(s"\nAs map: ${toMap(user)}")

  val config = Config("localhost", 8080, true, 3)
  println(s"Config map: ${toMap(config)}")

  // Tuple operations
  println()
  tupleOps()

  // Type-safe builder
  // PersonBuilder().build  // Won't compile! Missing fields
  // PersonBuilder().name("A").build  // Won't compile!
  // PersonBuilder().name("A").age(30).email("a@b").build  // Compiles!
  println(s"\nBuilder works type-safely at compile time")

Use Cases

  • Generic programming with Mirrors
  • Type-safe builder APIs
  • Heterogeneous data handling

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.