0
# Compile-Time Programming
1
2
The `scala.compiletime` package provides comprehensive utilities for compile-time metaprogramming, type-level computation, and constexpr-style programming in Scala 3.
3
4
## Core Compile-Time Functions
5
6
### Value Extraction and Constants
7
8
```scala { .api }
9
def erasedValue[T]: T
10
transparent inline def constValue[T]: T
11
transparent inline def constValueOpt[T]: Option[T]
12
inline def constValueTuple[T <: Tuple]: T
13
```
14
15
- **`erasedValue[T]`**: Pattern match on types without having actual values (used in inline matches)
16
- **`constValue[T]`**: Convert singleton types to their corresponding values at compile time
17
- **`constValueOpt[T]`**: Safe version of `constValue` that returns `None` if conversion fails
18
- **`constValueTuple[T]`**: Extract tuple of constant values from tuple type
19
20
### Given Summoning
21
22
```scala { .api }
23
transparent inline def summonFrom[T](f: Nothing => T): T
24
transparent inline def summonInline[T]: T
25
inline def summonAll[T <: Tuple]: T
26
```
27
28
- **`summonFrom[T]`**: Pattern match on available given instances
29
- **`summonInline[T]`**: Summon given value with delayed resolution until full inlining
30
- **`summonAll[T]`**: Summon all elements of a tuple type as a tuple of given instances
31
32
### Compile-Time Assertions and Debugging
33
34
```scala { .api }
35
inline def error(inline msg: String): Nothing
36
transparent inline def codeOf(arg: Any): String
37
inline def requireConst(inline x: Boolean | Byte | Short | Int | Long | Float | Double | Char | String): Unit
38
```
39
40
- **`error`**: Produce user-defined compile errors during inline expansion
41
- **`codeOf`**: Get string representation of code (for debugging and error messages)
42
- **`requireConst`**: Assert that a value is constant after inlining and constant folding
43
44
### Initialization and Deferred Values
45
46
```scala { .api }
47
def uninitialized: Nothing
48
def deferred: Nothing
49
def byName[T](x: => T): T
50
```
51
52
- **`uninitialized`**: Marker for uninitialized mutable fields (compile-time only)
53
- **`deferred`**: Marker for deferred given definitions in traits
54
- **`byName`**: Assertion for by-name parameters (used in nullability checking)
55
56
### Type Conversion
57
58
```scala { .api }
59
extension [T](x: T) transparent inline def asMatchable: x.type & Matchable
60
```
61
62
Cast values to be `Matchable` for pattern matching with unconstrained type parameters.
63
64
## Type-Level Operations
65
66
### Integer Type Operations
67
68
```scala { .api }
69
// Arithmetic operations
70
type +[X <: Int, Y <: Int] <: Int
71
type -[X <: Int, Y <: Int] <: Int
72
type *[X <: Int, Y <: Int] <: Int
73
type /[X <: Int, Y <: Int] <: Int
74
type %[X <: Int, Y <: Int] <: Int
75
76
// Comparison operations
77
type <[X <: Int, Y <: Int] <: Boolean
78
type <=[X <: Int, Y <: Int] <: Boolean
79
type >[X <: Int, Y <: Int] <: Boolean
80
type >=[X <: Int, Y <: Int] <: Boolean
81
82
// Utility operations
83
type S[X <: Int] <: Int // Successor (X + 1)
84
type Abs[X <: Int] <: Int // Absolute value
85
type Min[X <: Int, Y <: Int] <: Int // Minimum
86
type Max[X <: Int, Y <: Int] <: Int // Maximum
87
```
88
89
### Boolean Type Operations
90
91
```scala { .api }
92
type ![X <: Boolean] <: Boolean // Negation
93
type &&[X <: Boolean, Y <: Boolean] <: Boolean // Logical AND
94
type ||[X <: Boolean, Y <: Boolean] <: Boolean // Logical OR
95
type XOR[X <: Boolean, Y <: Boolean] <: Boolean // Exclusive OR
96
```
97
98
### String Type Operations
99
100
```scala { .api }
101
type +[X <: String, Y <: String] <: String // Concatenation
102
type Length[X <: String] <: Int // String length
103
type Substring[S <: String, IBeg <: Int, IEnd <: Int] <: String // Substring
104
type CharAt[S <: String, I <: Int] <: Char // Character at index
105
type Matches[S <: String, Regex <: String] <: Boolean // Regex matching
106
```
107
108
## Usage Examples
109
110
### Pattern Matching on Types
111
112
```scala
113
import scala.compiletime.*
114
115
inline def typeDescription[T]: String =
116
inline erasedValue[T] match
117
case _: String => "text"
118
case _: Int => "integer"
119
case _: Boolean => "boolean"
120
case _: EmptyTuple => "empty tuple"
121
case _: (h *: t) => s"tuple starting with ${typeDescription[h]}"
122
case _ => "unknown type"
123
124
// Usage
125
val desc1 = typeDescription[String] // "text"
126
val desc2 = typeDescription[Int *: String *: EmptyTuple] // "tuple starting with integer"
127
```
128
129
### Working with Constant Values
130
131
```scala
132
import scala.compiletime.*
133
134
// Convert singleton types to values
135
type Three = 3
136
type Hello = "hello"
137
138
val three: 3 = constValue[Three] // 3
139
val hello: "hello" = constValue[Hello] // "hello"
140
141
// Safe constant extraction
142
inline def safeConstant[T]: String =
143
constValueOpt[T] match
144
case Some(value) => s"Constant: $value"
145
case None => "Not a constant type"
146
147
val result1 = safeConstant[42] // "Constant: 42"
148
val result2 = safeConstant[String] // "Not a constant type"
149
150
// Working with tuple constants
151
type Numbers = (1, 2, 3)
152
val numbers: (1, 2, 3) = constValueTuple[Numbers]
153
```
154
155
### Compile-Time Assertions
156
157
```scala
158
import scala.compiletime.*
159
160
inline def safeDivision(inline numerator: Int, inline denominator: Int): Double =
161
requireConst(denominator) // Ensure denominator is known at compile time
162
163
inline if constValue[denominator.type] == 0 then
164
error("Division by zero detected at compile time")
165
else
166
numerator.toDouble / denominator.toDouble
167
168
// Usage
169
val result1 = safeDivision(10, 2) // Compiles: 5.0
170
// val result2 = safeDivision(10, 0) // Compile error: "Division by zero detected at compile time"
171
172
val x = 5
173
// val result3 = safeDivision(10, x) // Compile error: x is not constant
174
```
175
176
### Summoning Given Instances
177
178
```scala
179
import scala.compiletime.*
180
181
trait Show[T]:
182
def show(value: T): String
183
184
given Show[Int] with
185
def show(value: Int) = value.toString
186
187
given Show[String] with
188
def show(value: String) = s"\"$value\""
189
190
given Show[Boolean] with
191
def show(value: Boolean) = value.toString
192
193
// Summon specific instance
194
inline def showValue[T](value: T): String =
195
summonInline[Show[T]].show(value)
196
197
// Pattern match on available instances
198
inline def showAny[T](value: T): String =
199
summonFrom {
200
case given Show[T] => summon[Show[T]].show(value)
201
case _ => "No Show instance available"
202
}
203
204
// Summon multiple instances
205
inline def showTuple[T <: Tuple](tuple: T): List[String] =
206
summonAll[Tuple.Map[T, Show]].toList.zip(tuple.toList).map {
207
case (showInstance, value) => showInstance.asInstanceOf[Show[Any]].show(value)
208
}
209
210
val result1 = showValue(42) // "42"
211
val result2 = showAny("hello") // "\"hello\""
212
val result3 = showTuple((42, "world", true)) // List("42", "\"world\"", "true")
213
```
214
215
### Type-Level Arithmetic
216
217
```scala
218
import scala.compiletime.ops.int.*
219
220
type Two = 2
221
type Three = 3
222
type Five = Two + Three
223
type Six = Two * Three
224
type IsLess = Two < Three // true
225
type Successor = S[Two] // 3
226
227
// Compile-time computation
228
inline def factorial[N <: Int]: Int =
229
inline constValue[N] match
230
case 0 => 1
231
case 1 => 1
232
case n => n * factorial[N - 1]
233
234
val fact5: Int = factorial[5] // 120 (computed at compile time)
235
236
// Type-level validation
237
inline def validateArrayIndex[Size <: Int, Index <: Int]: Unit =
238
inline if constValue[Index >= Size] then
239
error("Array index out of bounds")
240
else if constValue[Index < 0] then
241
error("Negative array index")
242
243
validateArrayIndex[5, 2] // Compiles
244
// validateArrayIndex[5, 7] // Compile error: "Array index out of bounds"
245
```
246
247
### String Type Operations
248
249
```scala
250
import scala.compiletime.ops.string.*
251
252
type Hello = "Hello"
253
type World = "World"
254
type HelloWorld = Hello + " " + World // "Hello World"
255
type HelloLength = Length[Hello] // 5
256
type FirstChar = CharAt[Hello, 0] // 'H'
257
258
inline def greet[Name <: String]: String =
259
"Hello, " + constValue[Name] + "!"
260
261
val greeting = greet["Alice"] // "Hello, Alice!"
262
263
// Compile-time string validation
264
inline def validateEmail[Email <: String]: Unit =
265
inline if !constValue[Matches[Email, ".*@.*\\..*"]] then
266
error("Invalid email format")
267
268
validateEmail["user@example.com"] // Compiles
269
// validateEmail["invalid-email"] // Compile error: "Invalid email format"
270
```
271
272
### Code Generation and Debugging
273
274
```scala
275
import scala.compiletime.*
276
277
inline def logged[T](inline expr: T): T =
278
println(s"Evaluating: ${codeOf(expr)}")
279
expr
280
281
val result = logged(2 + 3 * 4)
282
// Prints: "Evaluating: 2.+(3.*(4))"
283
// Returns: 14
284
285
// Debug macro expansion
286
inline def debugType[T]: String =
287
inline erasedValue[T] match
288
case _: Int => s"Int type with code: ${codeOf(42)}"
289
case _: String => s"String type with code: ${codeOf("hello")}"
290
case _ => s"Other type"
291
```
292
293
### Advanced Generic Programming
294
295
```scala
296
import scala.compiletime.*
297
298
// Compile-time list processing
299
type NestedTuple = ((Int, String), (Boolean, Double), (Char, Float))
300
301
inline def processTuple[T <: Tuple]: String =
302
inline erasedValue[T] match
303
case _: EmptyTuple => "empty"
304
case _: (h *: t) =>
305
s"head: ${typeDescription[h]}, tail: ${processTuple[t]}"
306
307
inline def tupleSize[T <: Tuple]: Int =
308
inline erasedValue[T] match
309
case _: EmptyTuple => 0
310
case _: (h *: t) => 1 + tupleSize[t]
311
312
val description = processTuple[NestedTuple]
313
val size = tupleSize[NestedTuple] // 3
314
315
// Compile-time validation for type classes
316
inline def requireShow[T]: Unit =
317
summonFrom {
318
case given Show[T] => ()
319
case _ => error(s"No Show instance for type ${codeOf(??? : T)}")
320
}
321
322
inline def showIfPossible[T](value: T): String =
323
requireShow[T]
324
summon[Show[T]].show(value)
325
```
326
327
### Conditional Compilation
328
329
```scala
330
import scala.compiletime.*
331
332
inline def platformSpecific[T](value: T): String =
333
inline if constValue[scala.util.Properties.isWin] then
334
s"Windows: $value"
335
else if constValue[scala.util.Properties.isLinux] then
336
s"Linux: $value"
337
else
338
s"Other OS: $value"
339
340
// Feature flags at compile time
341
inline val FEATURE_ENABLED = true
342
343
inline def withFeature[T](inline enabled: T, inline disabled: T): T =
344
inline if FEATURE_ENABLED then enabled else disabled
345
346
val result = withFeature("Feature is on", "Feature is off")
347
```
348
349
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.