scalaadvanced

Type-Level Programming Basics

Explore Scala 3 type-level programming: match types, singleton types, and type lambdas.

scala
import scala.compiletime.{constValue, erasedValue, summonInline}

// Match types
type Elem[X] = X match
  case String      => Char
  case Array[t]    => t
  case List[t]     => t
  case Option[t]   => t

// Proof at type level
val charFromString: Elem[String] = 'a'
val intFromList: Elem[List[Int]] = 42
val strFromOption: Elem[Option[String]] = "hello"

// Recursive match types
type Flatten[X] = X match
  case List[List[t]] => Flatten[List[t]]
  case List[t]       => List[t]
  case t             => t

// Singleton types
def literal[T <: Singleton](value: T): T = value
val x: 42 = literal(42)
val s: "hello" = literal("hello")

// Type-level natural numbers
sealed trait Nat
class Zero extends Nat
class Succ[N <: Nat] extends Nat

type _0 = Zero
type _1 = Succ[_0]
type _2 = Succ[_1]
type _3 = Succ[_2]

// Type-safe sized vector
sealed trait HList
case object HNil extends HList
case class ::[+H, +T <: HList](head: H, tail: T) extends HList

type Concat[A <: HList, B <: HList] = A match
  case HNil.type => B
  case head :: tail => head :: Concat[tail, B]

// Compile-time operations
inline def typeOf[T]: String =
  inline erasedValue[T] match
    case _: Int    => "Int"
    case _: String => "String"
    case _: Double => "Double"
    case _         => "Unknown"

// Type class derivation helper
trait Size[T]:
  def size: Int

object Size:
  given Size[EmptyTuple] with
    def size: Int = 0

  given [H, T <: Tuple](using tailSize: Size[T]): Size[H *: T] with
    def size: Int = 1 + tailSize.size

def tupleSize[T <: Tuple](using s: Size[T]): Int = s.size

@main def run(): Unit =
  // Match types
  println(s"Elem[String]: $charFromString")
  println(s"Elem[List[Int]]: $intFromList")

  // Singleton types
  println(s"Literal: $x")

  // HList
  val hlist = ::(1, ::("hello", ::(true, HNil)))
  println(s"HList: $hlist")
  println(s"Head: ${hlist.head}")
  println(s"Tail head: ${hlist.tail.head}")

  // Compile-time
  println(typeOf[Int])
  println(typeOf[String])

  // Tuple size
  println(s"Tuple3 size: ${tupleSize[(Int, String, Boolean)]}")
  println(s"Tuple5 size: ${tupleSize[(Int, String, Boolean, Double, Char)]}")

Use Cases

  • Type-safe library APIs
  • Compile-time verification
  • Generic programming with type proofs

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.