or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

annotations.mdcompile-time.mdgeneric-programming.mdimmutable-arrays.mdindex.mdmacros.mdstructural-types.mdtuples.mdtype-safe-equality.md

annotations.mddocs/

0

# Annotation System

1

2

Scala 3 provides a rich annotation system for controlling compiler behavior, optimization hints, API design, and metaprogramming capabilities.

3

4

## Experimental and Stability Annotations

5

6

### Experimental Features

7

8

```scala { .api }

9

class experimental extends StaticAnnotation

10

class alpha extends StaticAnnotation

11

```

12

13

- **`@experimental`**: Mark APIs that are experimental and may change

14

- **`@alpha`**: Mark APIs that are in alpha stage and unstable

15

16

Usage:

17

```scala

18

@experimental

19

def newFeature(): String = "This API may change"

20

21

@alpha

22

class BetaClass:

23

def method(): Int = 42

24

25

// Usage requires explicit import

26

import scala.language.experimental.featureName

27

val result = newFeature() // Requires experimental import

28

```

29

30

## Capability System Annotations

31

32

### Capability Marking

33

34

```scala { .api }

35

class capability extends StaticAnnotation

36

```

37

38

Mark classes as capability classes for the capture checking system:

39

40

```scala

41

@capability

42

class FileReader

43

44

@capability

45

class NetworkAccess

46

47

def readFile(using FileReader): String = "file content"

48

def fetchData(using NetworkAccess): String = "network data"

49

```

50

51

## Method and Constructor Annotations

52

53

### Constructor Restrictions

54

55

```scala { .api }

56

class constructorOnly extends StaticAnnotation

57

```

58

59

Restrict annotation usage to constructors only:

60

61

```scala

62

@constructorOnly

63

class ValidatedInt(value: Int):

64

require(value >= 0, "Must be non-negative")

65

66

// Can only be used in constructors

67

class Person(@ValidatedInt age: Int, name: String)

68

```

69

70

### Method Naming

71

72

```scala { .api }

73

class targetName(name: String) extends StaticAnnotation

74

```

75

76

Override the name used for the method in compiled bytecode:

77

78

```scala

79

class Calculator:

80

@targetName("add")

81

def +(x: Int, y: Int): Int = x + y

82

83

@targetName("multiply")

84

def *(x: Int, y: Int): Int = x * y

85

86

// Bytecode will have methods named "add" and "multiply"

87

val calc = Calculator()

88

val sum = calc + (5, 3) // Calls "add" in bytecode

89

val product = calc * (4, 7) // Calls "multiply" in bytecode

90

```

91

92

### Static Method Generation

93

94

```scala { .api }

95

class static extends StaticAnnotation

96

```

97

98

Generate static methods in the bytecode:

99

100

```scala

101

object MathUtils:

102

@static

103

def square(x: Int): Int = x * x

104

105

@static

106

def cube(x: Double): Double = x * x * x

107

108

// Generates static methods in Java bytecode for Java interop

109

// Java code can call: MathUtils.square(5)

110

```

111

112

## Optimization and Performance Annotations

113

114

### Thread Safety

115

116

```scala { .api }

117

class threadUnsafe extends StaticAnnotation

118

```

119

120

Mark code as thread-unsafe for optimization hints:

121

122

```scala

123

@threadUnsafe

124

class FastCounter:

125

private var count = 0

126

def increment(): Unit = count += 1

127

def get: Int = count

128

129

// Compiler can apply single-threaded optimizations

130

```

131

132

### Trait Transparency

133

134

```scala { .api }

135

class transparentTrait extends StaticAnnotation

136

```

137

138

Mark traits as transparent for better optimization:

139

140

```scala

141

@transparentTrait

142

trait Logging:

143

def log(msg: String): Unit = println(s"[LOG] $msg")

144

145

class Service extends Logging:

146

def process(): Unit =

147

log("Processing started")

148

// Processing logic

149

log("Processing completed")

150

```

151

152

### Loop Unrolling

153

154

```scala { .api }

155

class unroll extends StaticAnnotation

156

```

157

158

Hint to the compiler to unroll loops or recursive calls:

159

160

```scala

161

@unroll

162

def factorial(n: Int): Int =

163

if n <= 1 then 1

164

else n * factorial(n - 1)

165

166

@unroll

167

def sumArray(arr: Array[Int]): Int =

168

var sum = 0

169

var i = 0

170

while i < arr.length do

171

sum += arr(i)

172

i += 1

173

sum

174

```

175

176

## Binary Compatibility Annotations

177

178

### Public Visibility Control

179

180

```scala { .api }

181

class publicInBinary extends StaticAnnotation

182

```

183

184

Force methods to be public in bytecode even if they're private in Scala:

185

186

```scala

187

class Library:

188

@publicInBinary

189

private def internalMethod(): String = "internal"

190

191

def publicMethod(): String = internalMethod()

192

193

// internalMethod will be public in Java bytecode for framework access

194

```

195

196

### Initialization Control

197

198

```scala { .api }

199

class init extends StaticAnnotation

200

```

201

202

Control initialization order and behavior:

203

204

```scala

205

class Database:

206

@init

207

def initialize(): Unit =

208

// Critical initialization code

209

println("Database initialized")

210

211

def query(sql: String): String =

212

// Query implementation

213

s"Result for: $sql"

214

```

215

216

## Meta-Programming Annotations

217

218

### Macro Annotations

219

220

```scala { .api }

221

trait MacroAnnotation extends StaticAnnotation

222

```

223

224

Base trait for creating macro annotations that transform code at compile time:

225

226

```scala

227

import scala.annotation.MacroAnnotation

228

import scala.quoted.*

229

230

class toString extends MacroAnnotation:

231

def transform(using Quotes)(tree: quotes.reflect.Definition): List[quotes.reflect.Definition] =

232

import quotes.reflect.*

233

234

tree match

235

case ClassDef(name, constr, parents, self, body) =>

236

// Generate toString method

237

val toStringMethod = DefDef.copy(tree.asInstanceOf[DefDef])(

238

name = "toString",

239

paramss = List(List()),

240

tpt = TypeTree.of[String],

241

rhs = Some(Literal(StringConstant(s"Instance of $name")))

242

)

243

List(ClassDef.copy(tree.asInstanceOf[ClassDef])(

244

name, constr, parents, self, body :+ toStringMethod

245

))

246

case _ => List(tree)

247

248

// Usage

249

@toString

250

class Person(name: String, age: Int)

251

252

val person = Person("Alice", 30)

253

println(person.toString) // "Instance of Person"

254

```

255

256

### Refining Annotations

257

258

```scala { .api }

259

trait RefiningAnnotation extends StaticAnnotation

260

```

261

262

Base trait for annotations that refine types:

263

264

```scala

265

import scala.annotation.RefiningAnnotation

266

267

class positive extends RefiningAnnotation

268

269

def process(@positive x: Int): Int =

270

require(x > 0)

271

x * 2

272

273

// The annotation refines the type to indicate positive integers

274

val result = process(5) // OK

275

// val invalid = process(-3) // Could be caught by static analysis

276

```

277

278

## Usage Examples

279

280

### Combining Annotations

281

282

```scala

283

@experimental

284

@targetName("advancedCalculation")

285

class Calculator:

286

@threadUnsafe

287

@unroll

288

private def fastCompute(n: Int): Long =

289

if n <= 1 then 1L

290

else n * fastCompute(n - 1)

291

292

@publicInBinary

293

@static

294

def compute(x: Int): Long = fastCompute(x)

295

296

// This class demonstrates multiple annotation types:

297

// - Experimental API that may change

298

// - Custom bytecode method name

299

// - Thread-unsafe optimization

300

// - Loop unrolling hint

301

// - Public binary visibility for private method

302

// - Static method generation

303

```

304

305

### Custom Domain Annotations

306

307

```scala

308

// Define domain-specific annotations

309

class authenticated extends StaticAnnotation

310

class cached(timeoutSeconds: Int) extends StaticAnnotation

311

class rateLimit(requestsPerMinute: Int) extends StaticAnnotation

312

313

class UserService:

314

@authenticated

315

@cached(300) // 5 minute cache

316

def getUserProfile(userId: String): UserProfile =

317

// Expensive database operation

318

UserProfile(userId, "User Name")

319

320

@authenticated

321

@rateLimit(100) // 100 requests per minute

322

def updateUser(userId: String, data: UserData): Unit =

323

// Update user data

324

println(s"Updating user $userId")

325

326

// These annotations can be processed by frameworks or macros

327

// to automatically implement cross-cutting concerns

328

```

329

330

### Validation Annotations

331

332

```scala

333

class min(value: Int) extends StaticAnnotation

334

class max(value: Int) extends StaticAnnotation

335

class email extends StaticAnnotation

336

class notNull extends StaticAnnotation

337

338

case class User(

339

@notNull @min(1) name: String,

340

@min(0) @max(150) age: Int,

341

@email email: String

342

)

343

344

// These annotations can be processed at compile time

345

// to generate validation logic

346

```

347

348

### API Evolution Annotations

349

350

```scala

351

class deprecated(message: String, since: String) extends StaticAnnotation

352

class migration(from: String, to: String) extends StaticAnnotation

353

354

class APIService:

355

@deprecated("Use newMethod instead", "3.1.0")

356

def oldMethod(): String = "old implementation"

357

358

@migration(from = "oldMethod", to = "newMethod")

359

def newMethod(): String = "new implementation"

360

361

@experimental

362

def futureMethod(): String = "may change in future versions"

363

364

// Provides clear evolution path for APIs

365

val service = APIService()

366

val result1 = service.oldMethod() // Compiler warning about deprecation

367

val result2 = service.newMethod() // Current recommended approach

368

val result3 = service.futureMethod() // Requires experimental import

369

```

370

371

### Framework Integration Annotations

372

373

```scala

374

// HTTP framework annotations

375

class GET(path: String) extends StaticAnnotation

376

class POST(path: String) extends StaticAnnotation

377

class PathParam(name: String) extends StaticAnnotation

378

class QueryParam(name: String) extends StaticAnnotation

379

380

class UserController:

381

@GET("/users/{id}")

382

def getUser(@PathParam("id") userId: String): User =

383

User(userId, "User Name")

384

385

@POST("/users")

386

def createUser(user: User): User =

387

// Create user logic

388

user

389

390

@GET("/users")

391

def searchUsers(@QueryParam("name") name: String): List[User] =

392

// Search logic

393

List(User("1", name))

394

395

// Database mapping annotations

396

class Table(name: String) extends StaticAnnotation

397

class Column(name: String) extends StaticAnnotation

398

class Id extends StaticAnnotation

399

400

@Table("users")

401

case class UserEntity(

402

@Id @Column("user_id") id: String,

403

@Column("full_name") name: String,

404

@Column("email_address") email: String

405

)

406

```

407

408

The annotation system in Scala 3 provides powerful capabilities for controlling compiler behavior, enabling metaprogramming, and integrating with frameworks while maintaining type safety and compile-time verification.