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

annotations.mddocs/

Annotation System

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

Experimental and Stability Annotations

Experimental Features

class experimental extends StaticAnnotation
class alpha extends StaticAnnotation
  • @experimental: Mark APIs that are experimental and may change
  • @alpha: Mark APIs that are in alpha stage and unstable

Usage:

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

@alpha
class BetaClass:
  def method(): Int = 42

// Usage requires explicit import
import scala.language.experimental.featureName
val result = newFeature()  // Requires experimental import

Capability System Annotations

Capability Marking

class capability extends StaticAnnotation

Mark classes as capability classes for the capture checking system:

@capability
class FileReader
  
@capability  
class NetworkAccess

def readFile(using FileReader): String = "file content"
def fetchData(using NetworkAccess): String = "network data"

Method and Constructor Annotations

Constructor Restrictions

class constructorOnly extends StaticAnnotation

Restrict annotation usage to constructors only:

@constructorOnly
class ValidatedInt(value: Int):
  require(value >= 0, "Must be non-negative")

// Can only be used in constructors
class Person(@ValidatedInt age: Int, name: String)

Method Naming

class targetName(name: String) extends StaticAnnotation

Override the name used for the method in compiled bytecode:

class Calculator:
  @targetName("add")
  def +(x: Int, y: Int): Int = x + y
  
  @targetName("multiply") 
  def *(x: Int, y: Int): Int = x * y

// Bytecode will have methods named "add" and "multiply"
val calc = Calculator()
val sum = calc + (5, 3)      // Calls "add" in bytecode
val product = calc * (4, 7)  // Calls "multiply" in bytecode

Static Method Generation

class static extends StaticAnnotation

Generate static methods in the bytecode:

object MathUtils:
  @static
  def square(x: Int): Int = x * x
  
  @static
  def cube(x: Double): Double = x * x * x

// Generates static methods in Java bytecode for Java interop
// Java code can call: MathUtils.square(5)

Optimization and Performance Annotations

Thread Safety

class threadUnsafe extends StaticAnnotation

Mark code as thread-unsafe for optimization hints:

@threadUnsafe
class FastCounter:
  private var count = 0
  def increment(): Unit = count += 1
  def get: Int = count

// Compiler can apply single-threaded optimizations

Trait Transparency

class transparentTrait extends StaticAnnotation

Mark traits as transparent for better optimization:

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

class Service extends Logging:
  def process(): Unit = 
    log("Processing started")
    // Processing logic
    log("Processing completed")

Loop Unrolling

class unroll extends StaticAnnotation

Hint to the compiler to unroll loops or recursive calls:

@unroll
def factorial(n: Int): Int = 
  if n <= 1 then 1 
  else n * factorial(n - 1)

@unroll  
def sumArray(arr: Array[Int]): Int =
  var sum = 0
  var i = 0
  while i < arr.length do
    sum += arr(i)
    i += 1
  sum

Binary Compatibility Annotations

Public Visibility Control

class publicInBinary extends StaticAnnotation

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

class Library:
  @publicInBinary
  private def internalMethod(): String = "internal"
  
  def publicMethod(): String = internalMethod()

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

Initialization Control

class init extends StaticAnnotation

Control initialization order and behavior:

class Database:
  @init
  def initialize(): Unit = 
    // Critical initialization code
    println("Database initialized")
  
  def query(sql: String): String = 
    // Query implementation
    s"Result for: $sql"

Meta-Programming Annotations

Macro Annotations

trait MacroAnnotation extends StaticAnnotation

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

import scala.annotation.MacroAnnotation
import scala.quoted.*

class toString extends MacroAnnotation:
  def transform(using Quotes)(tree: quotes.reflect.Definition): List[quotes.reflect.Definition] =
    import quotes.reflect.*
    
    tree match
      case ClassDef(name, constr, parents, self, body) =>
        // Generate toString method
        val toStringMethod = DefDef.copy(tree.asInstanceOf[DefDef])(
          name = "toString",
          paramss = List(List()),
          tpt = TypeTree.of[String],
          rhs = Some(Literal(StringConstant(s"Instance of $name")))
        )
        List(ClassDef.copy(tree.asInstanceOf[ClassDef])(
          name, constr, parents, self, body :+ toStringMethod
        ))
      case _ => List(tree)

// Usage
@toString
class Person(name: String, age: Int)

val person = Person("Alice", 30)
println(person.toString)  // "Instance of Person"

Refining Annotations

trait RefiningAnnotation extends StaticAnnotation

Base trait for annotations that refine types:

import scala.annotation.RefiningAnnotation

class positive extends RefiningAnnotation

def process(@positive x: Int): Int = 
  require(x > 0)
  x * 2

// The annotation refines the type to indicate positive integers
val result = process(5)  // OK
// val invalid = process(-3)  // Could be caught by static analysis

Usage Examples

Combining Annotations

@experimental
@targetName("advancedCalculation")
class Calculator:
  @threadUnsafe
  @unroll
  private def fastCompute(n: Int): Long = 
    if n <= 1 then 1L
    else n * fastCompute(n - 1)
  
  @publicInBinary
  @static
  def compute(x: Int): Long = fastCompute(x)

// This class demonstrates multiple annotation types:
// - Experimental API that may change
// - Custom bytecode method name
// - Thread-unsafe optimization
// - Loop unrolling hint
// - Public binary visibility for private method
// - Static method generation

Custom Domain Annotations

// Define domain-specific annotations
class authenticated extends StaticAnnotation
class cached(timeoutSeconds: Int) extends StaticAnnotation  
class rateLimit(requestsPerMinute: Int) extends StaticAnnotation

class UserService:
  @authenticated
  @cached(300)  // 5 minute cache
  def getUserProfile(userId: String): UserProfile = 
    // Expensive database operation
    UserProfile(userId, "User Name")
  
  @authenticated
  @rateLimit(100)  // 100 requests per minute
  def updateUser(userId: String, data: UserData): Unit = 
    // Update user data
    println(s"Updating user $userId")

// These annotations can be processed by frameworks or macros
// to automatically implement cross-cutting concerns

Validation Annotations

class min(value: Int) extends StaticAnnotation
class max(value: Int) extends StaticAnnotation
class email extends StaticAnnotation
class notNull extends StaticAnnotation

case class User(
  @notNull @min(1) name: String,
  @min(0) @max(150) age: Int,
  @email email: String
)

// These annotations can be processed at compile time
// to generate validation logic

API Evolution Annotations

class deprecated(message: String, since: String) extends StaticAnnotation
class migration(from: String, to: String) extends StaticAnnotation

class APIService:
  @deprecated("Use newMethod instead", "3.1.0")
  def oldMethod(): String = "old implementation"
  
  @migration(from = "oldMethod", to = "newMethod")
  def newMethod(): String = "new implementation"
  
  @experimental
  def futureMethod(): String = "may change in future versions"

// Provides clear evolution path for APIs
val service = APIService()
val result1 = service.oldMethod()     // Compiler warning about deprecation
val result2 = service.newMethod()     // Current recommended approach
val result3 = service.futureMethod()  // Requires experimental import

Framework Integration Annotations

// HTTP framework annotations
class GET(path: String) extends StaticAnnotation
class POST(path: String) extends StaticAnnotation
class PathParam(name: String) extends StaticAnnotation
class QueryParam(name: String) extends StaticAnnotation

class UserController:
  @GET("/users/{id}")
  def getUser(@PathParam("id") userId: String): User = 
    User(userId, "User Name")
  
  @POST("/users")
  def createUser(user: User): User = 
    // Create user logic
    user
  
  @GET("/users")
  def searchUsers(@QueryParam("name") name: String): List[User] = 
    // Search logic
    List(User("1", name))

// Database mapping annotations
class Table(name: String) extends StaticAnnotation
class Column(name: String) extends StaticAnnotation
class Id extends StaticAnnotation

@Table("users")
case class UserEntity(
  @Id @Column("user_id") id: String,
  @Column("full_name") name: String,
  @Column("email_address") email: String
)

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.