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

compile-time.mddocs/

Compile-Time Programming

The scala.compiletime package provides comprehensive utilities for compile-time metaprogramming, type-level computation, and constexpr-style programming in Scala 3.

Core Compile-Time Functions

Value Extraction and Constants

def erasedValue[T]: T
transparent inline def constValue[T]: T
transparent inline def constValueOpt[T]: Option[T]
inline def constValueTuple[T <: Tuple]: T
  • erasedValue[T]: Pattern match on types without having actual values (used in inline matches)
  • constValue[T]: Convert singleton types to their corresponding values at compile time
  • constValueOpt[T]: Safe version of constValue that returns None if conversion fails
  • constValueTuple[T]: Extract tuple of constant values from tuple type

Given Summoning

transparent inline def summonFrom[T](f: Nothing => T): T
transparent inline def summonInline[T]: T
inline def summonAll[T <: Tuple]: T
  • summonFrom[T]: Pattern match on available given instances
  • summonInline[T]: Summon given value with delayed resolution until full inlining
  • summonAll[T]: Summon all elements of a tuple type as a tuple of given instances

Compile-Time Assertions and Debugging

inline def error(inline msg: String): Nothing
transparent inline def codeOf(arg: Any): String
inline def requireConst(inline x: Boolean | Byte | Short | Int | Long | Float | Double | Char | String): Unit
  • error: Produce user-defined compile errors during inline expansion
  • codeOf: Get string representation of code (for debugging and error messages)
  • requireConst: Assert that a value is constant after inlining and constant folding

Initialization and Deferred Values

def uninitialized: Nothing
def deferred: Nothing
def byName[T](x: => T): T
  • uninitialized: Marker for uninitialized mutable fields (compile-time only)
  • deferred: Marker for deferred given definitions in traits
  • byName: Assertion for by-name parameters (used in nullability checking)

Type Conversion

extension [T](x: T) transparent inline def asMatchable: x.type & Matchable

Cast values to be Matchable for pattern matching with unconstrained type parameters.

Type-Level Operations

Integer Type Operations

// Arithmetic operations
type +[X <: Int, Y <: Int] <: Int
type -[X <: Int, Y <: Int] <: Int
type *[X <: Int, Y <: Int] <: Int
type /[X <: Int, Y <: Int] <: Int
type %[X <: Int, Y <: Int] <: Int

// Comparison operations
type <[X <: Int, Y <: Int] <: Boolean
type <=[X <: Int, Y <: Int] <: Boolean
type >[X <: Int, Y <: Int] <: Boolean
type >=[X <: Int, Y <: Int] <: Boolean

// Utility operations
type S[X <: Int] <: Int               // Successor (X + 1)
type Abs[X <: Int] <: Int             // Absolute value
type Min[X <: Int, Y <: Int] <: Int   // Minimum
type Max[X <: Int, Y <: Int] <: Int   // Maximum

Boolean Type Operations

type ![X <: Boolean] <: Boolean                    // Negation
type &&[X <: Boolean, Y <: Boolean] <: Boolean     // Logical AND
type ||[X <: Boolean, Y <: Boolean] <: Boolean     // Logical OR
type XOR[X <: Boolean, Y <: Boolean] <: Boolean    // Exclusive OR

String Type Operations

type +[X <: String, Y <: String] <: String                           // Concatenation
type Length[X <: String] <: Int                                      // String length
type Substring[S <: String, IBeg <: Int, IEnd <: Int] <: String      // Substring
type CharAt[S <: String, I <: Int] <: Char                          // Character at index
type Matches[S <: String, Regex <: String] <: Boolean               // Regex matching

Usage Examples

Pattern Matching on Types

import scala.compiletime.*

inline def typeDescription[T]: String = 
  inline erasedValue[T] match
    case _: String => "text"
    case _: Int => "integer" 
    case _: Boolean => "boolean"
    case _: EmptyTuple => "empty tuple"
    case _: (h *: t) => s"tuple starting with ${typeDescription[h]}"
    case _ => "unknown type"

// Usage
val desc1 = typeDescription[String]              // "text"
val desc2 = typeDescription[Int *: String *: EmptyTuple]  // "tuple starting with integer"

Working with Constant Values

import scala.compiletime.*

// Convert singleton types to values
type Three = 3
type Hello = "hello"

val three: 3 = constValue[Three]                 // 3
val hello: "hello" = constValue[Hello]           // "hello"

// Safe constant extraction
inline def safeConstant[T]: String = 
  constValueOpt[T] match
    case Some(value) => s"Constant: $value"
    case None => "Not a constant type"

val result1 = safeConstant[42]                   // "Constant: 42"
val result2 = safeConstant[String]               // "Not a constant type"

// Working with tuple constants
type Numbers = (1, 2, 3)
val numbers: (1, 2, 3) = constValueTuple[Numbers]

Compile-Time Assertions

import scala.compiletime.*

inline def safeDivision(inline numerator: Int, inline denominator: Int): Double =
  requireConst(denominator)  // Ensure denominator is known at compile time
  
  inline if constValue[denominator.type] == 0 then
    error("Division by zero detected at compile time")
  else
    numerator.toDouble / denominator.toDouble

// Usage
val result1 = safeDivision(10, 2)        // Compiles: 5.0
// val result2 = safeDivision(10, 0)     // Compile error: "Division by zero detected at compile time"

val x = 5
// val result3 = safeDivision(10, x)     // Compile error: x is not constant

Summoning Given Instances

import scala.compiletime.*

trait Show[T]:
  def show(value: T): String

given Show[Int] with
  def show(value: Int) = value.toString

given Show[String] with
  def show(value: String) = s"\"$value\""

given Show[Boolean] with
  def show(value: Boolean) = value.toString

// Summon specific instance
inline def showValue[T](value: T): String = 
  summonInline[Show[T]].show(value)

// Pattern match on available instances
inline def showAny[T](value: T): String = 
  summonFrom {
    case given Show[T] => summon[Show[T]].show(value)
    case _ => "No Show instance available"
  }

// Summon multiple instances
inline def showTuple[T <: Tuple](tuple: T): List[String] = 
  summonAll[Tuple.Map[T, Show]].toList.zip(tuple.toList).map {
    case (showInstance, value) => showInstance.asInstanceOf[Show[Any]].show(value)
  }

val result1 = showValue(42)                      // "42"
val result2 = showAny("hello")                   // "\"hello\""
val result3 = showTuple((42, "world", true))     // List("42", "\"world\"", "true")

Type-Level Arithmetic

import scala.compiletime.ops.int.*

type Two = 2
type Three = 3
type Five = Two + Three
type Six = Two * Three
type IsLess = Two < Three       // true
type Successor = S[Two]         // 3

// Compile-time computation
inline def factorial[N <: Int]: Int = 
  inline constValue[N] match
    case 0 => 1
    case 1 => 1
    case n => n * factorial[N - 1]

val fact5: Int = factorial[5]                    // 120 (computed at compile time)

// Type-level validation
inline def validateArrayIndex[Size <: Int, Index <: Int]: Unit =
  inline if constValue[Index >= Size] then
    error("Array index out of bounds")
  else if constValue[Index < 0] then
    error("Negative array index")

validateArrayIndex[5, 2]  // Compiles
// validateArrayIndex[5, 7]  // Compile error: "Array index out of bounds"

String Type Operations

import scala.compiletime.ops.string.*

type Hello = "Hello"
type World = "World"  
type HelloWorld = Hello + " " + World    // "Hello World"
type HelloLength = Length[Hello]         // 5
type FirstChar = CharAt[Hello, 0]        // 'H'

inline def greet[Name <: String]: String = 
  "Hello, " + constValue[Name] + "!"

val greeting = greet["Alice"]                    // "Hello, Alice!"

// Compile-time string validation
inline def validateEmail[Email <: String]: Unit =
  inline if !constValue[Matches[Email, ".*@.*\\..*"]] then
    error("Invalid email format")

validateEmail["user@example.com"]  // Compiles
// validateEmail["invalid-email"]  // Compile error: "Invalid email format"

Code Generation and Debugging

import scala.compiletime.*

inline def logged[T](inline expr: T): T = 
  println(s"Evaluating: ${codeOf(expr)}")
  expr

val result = logged(2 + 3 * 4)
// Prints: "Evaluating: 2.+(3.*(4))"
// Returns: 14

// Debug macro expansion
inline def debugType[T]: String = 
  inline erasedValue[T] match
    case _: Int => s"Int type with code: ${codeOf(42)}"
    case _: String => s"String type with code: ${codeOf("hello")}"
    case _ => s"Other type"

Advanced Generic Programming

import scala.compiletime.*

// Compile-time list processing
type NestedTuple = ((Int, String), (Boolean, Double), (Char, Float))

inline def processTuple[T <: Tuple]: String = 
  inline erasedValue[T] match
    case _: EmptyTuple => "empty"
    case _: (h *: t) => 
      s"head: ${typeDescription[h]}, tail: ${processTuple[t]}"

inline def tupleSize[T <: Tuple]: Int = 
  inline erasedValue[T] match
    case _: EmptyTuple => 0
    case _: (h *: t) => 1 + tupleSize[t]

val description = processTuple[NestedTuple]
val size = tupleSize[NestedTuple]               // 3

// Compile-time validation for type classes
inline def requireShow[T]: Unit =
  summonFrom {
    case given Show[T] => ()
    case _ => error(s"No Show instance for type ${codeOf(??? : T)}")
  }

inline def showIfPossible[T](value: T): String =
  requireShow[T]
  summon[Show[T]].show(value)

Conditional Compilation

import scala.compiletime.*

inline def platformSpecific[T](value: T): String =
  inline if constValue[scala.util.Properties.isWin] then
    s"Windows: $value"
  else if constValue[scala.util.Properties.isLinux] then  
    s"Linux: $value"
  else
    s"Other OS: $value"

// Feature flags at compile time
inline val FEATURE_ENABLED = true

inline def withFeature[T](inline enabled: T, inline disabled: T): T =
  inline if FEATURE_ENABLED then enabled else disabled

val result = withFeature("Feature is on", "Feature is off")

The compile-time programming capabilities in Scala 3 enable powerful metaprogramming techniques, allowing developers to move computations and validations from runtime to compile time, resulting in better performance and stronger type safety guarantees.