Skip to content

Custom Annotations & Scope Injection

Flame empowers systems engineers to declare custom annotations using the annotation keyword. Annotations in Flame are first-class runtime and compile-time constructs: they accept typed parameters, execute initialization logic, and return data that is automatically injected directly into the decorated function’s local scope.


You define custom annotations using the annotation keyword, specifying parameters and an explicit return type:

// Declare an annotation that returns a formatted logger prefix string
export annotation Logger(prefix: String) -> String {
return $"[{prefix}] "
}
// Declare an annotation that initializes a route configuration formula
export annotation Route(method: String, path: String) -> Formula {
return formula {
method: method,
path: path,
timestamp: 1785940000
}
}

2. Automatic Value Injection into Functions

Section titled “2. Automatic Value Injection into Functions”

When you decorate a function with a custom annotation, Flame evaluates the annotation at runtime and automatically injects the returned value directly into the function’s local scope as a mutable variable named after the annotation (accessible via lowercase or PascalCase):

@Logger("AUTH_SERVICE")
fn authenticate_user(username: String) {
// 'logger' (or 'Logger') is automatically available in local scope from @Logger!
print(logger + "Authenticating user: " + username)
// The injected variable is mutable and can be dynamically modified in scope
logger = logger + "[SECURE] "
print(logger + "Access granted.")
}
authenticate_user("admin")
[AUTH_SERVICE] Authenticating user: admin
[AUTH_SERVICE] [SECURE] Access granted.

3. Mutable State & Complex Formula Injection

Section titled “3. Mutable State & Complex Formula Injection”

Custom annotations can return complex formulas, structs, or state containers, making them invaluable for middleware pipelines and contextual dependency injection:

export annotation Context(service_name: String) -> Formula {
return formula {
service: service_name,
request_count: 0
}
}
@Context("PAYMENT_GATEWAY")
fn process_transaction(amount: Float) {
// Access formula properties directly from the injected 'context' variable
context.request_count += 1
print($"Service: {context.service}, Requests: {context.request_count}, Amount: ${amount}")
}
process_transaction(99.95)

4. Metaprogramming with std.annotation & AnnotationContext

Section titled “4. Metaprogramming with std.annotation & AnnotationContext”

Flame provides a metaprogramming standard library module (std.annotation) enabling custom annotations to inspect targets, retrieve callable references, and dynamically transform or wrap functions.

Inside any custom annotation body, call annotation.context() to retrieve structured context:

import std.annotation
annotation Route(path: String) {
let ctx = annotation.context()
// Access target declaration metadata:
let name = ctx.target.name // String (e.g. "get_users")
let kind = ctx.target.kind // String: "function", "struct", "enum", "variable"
let params = ctx.target.parameters // [ParameterInfo]
let ret = ctx.target.return_type // String
let annos = ctx.target.annotations // [AnnotationInfo]
// Inspect compiler & build configuration:
let compiler = ctx.compiler.name // "flame"
let platform = ctx.build.platform // e.g. "linux", "darwin", "windows"
let arch = ctx.build.arch // e.g. "x86_64", "aarch64"
}

Obtaining Target Function References (ctx.target.ref())

Section titled “Obtaining Target Function References (ctx.target.ref())”

For web frameworks, dependency injection, and router dispatchers, ctx.target.ref() yields a callable reference to the annotated function:

let routes: [Formula] = []
annotation GET(path: String) {
let ctx = annotation.context()
let handler = ctx.target.ref()
routes.push(formula {
method: "GET",
path: path,
handler: handler
})
}
@GET(path = "/api/v1/health")
fn health_check() -> String {
return "ok"
}
// Router execution:
let res = routes[0].handler() // returns "ok"

Transforming Functions (ctx.target.transform())

Section titled “Transforming Functions (ctx.target.transform())”

Custom annotations can wrap, intercept, or modify the execution of the target function using ctx.target.transform:

annotation Logged {
let ctx = annotation.context()
// Wrap the target with an interceptor closure receiving the original ref and arguments:
ctx.target.transform(
(ref, text: String) {
print($"[LOG] Calling {ctx.target.name} with: {text}")
let result = ref(text)
return $"[LOGGED: {result}]"
}
)
}
@Logged
fn greet(name: String) -> String {
return $"Hello, {name}!"
}
greet("Flame") // Prints log and returns "[LOGGED: Hello, Flame!]"

Flame also comes packed with native annotations for testing (@Test, @Setup, @Cleanup, @ExpectPanic) and declarative CLI building (@Cli, @Command).

To learn how to use @Cli and @Command to assemble powerful interactive system utilities and subcommands with zero boilerplate, visit the comprehensive reference on Built-in Annotations & CLI Builder.