+++ title = "Annotations" weight = 9 +++ Annotations use the `@` symbol and provide metadata for external tooling. MLF itself assigns no semantic meaning to most annotations - they're purely for tools, linters, code generators, and other processors. ## Annotation Syntax Three forms of annotations are supported: ### Simple Annotation ```mlf @deprecated record oldRecord { field: string, } ``` ### Positional Arguments ```mlf @since(1, 2, 0) @doc("https://example.com/docs") record example { field: string, } ``` Arguments can be: - **Strings**: `"value"` - **Numbers**: `42`, `3.14` - **Booleans**: `true`, `false` ### Named Arguments ```mlf @validate(min: 0, max: 100, strict: true) @cache(ttl: 3600, strategy: "lru") record example { field: integer, } ``` ## Annotation Placement Annotations can be placed on: - Records - Def Types - Inline Types - Tokens - Queries - Procedures - Subscriptions - Fields within records/types **Example:** ```mlf /// A user profile @table(name: "profiles", indexes: "did,handle") record profile { /// User's DID @indexed did!: Did, /// Display name (optional) @sensitive(pii: true) displayName: string, } ``` ## Annotation Semantics Annotations in MLF are interpreted by whatever consumes them - whether that's the MLF compiler itself or external code generators. ### Bare Annotations **Bare annotations** (without generator selectors) are visible to **all generators** and each can interpret them as needed: ```mlf // Visible to all generators - each interprets as appropriate @deprecated query foo(); ``` MLF itself recognizes the **`@main` annotation** for resolving naming conflicts. See [Important Info](/docs/language-guide/important-info/#the-main-definition) for details. ### Generator Selectors To target **specific generators**, use the generator selector syntax with a colon: ```mlf // Only for rust generator @rust:deprecated query bar(); // Only for typescript generator @typescript:deprecated query baz(); ``` ### Multiple Generator Selectors You can apply an annotation to multiple specific generators using comma-separated selectors: ```mlf // For both rust AND typescript @rust,typescript:deprecated query qux(); ``` Alternatively, you can write separate annotations: ```mlf // Equivalent to above @rust:deprecated @typescript:deprecated query qux(); ``` **Common generator selectors:** - `@rust:*` - Rust code generator annotations - `@typescript:*` - TypeScript code generator annotations - `@go:*` - Go code generator annotations ## MLF Built-in Annotations MLF's lexicon generator recognizes specific annotations that affect the generated ATProto JSON lexicon: ### `@key` for Records Controls the record key type. Defaults to `"tid"` if not specified. ```mlf // Use literal "self" as the record key @key("literal:self") record profile { name!: string, } // Use timestamp-based identifier (default) record post { text!: string, } ``` **Common key values:** - `"tid"` - Timestamp-based identifier (default) - `"literal:self"` - The record key is literally "self" - Custom values as needed for your schema ### `@encoding` for Queries and Procedures Controls MIME type encoding for XRPC input and output. Defaults to `"application/json"` if not specified. **Positional syntax** (applies to output for queries, both input/output for procedures): ```mlf @encoding("application/cbor") query getData(): string; @encoding("application/json") procedure upload(data!: string): string; ``` **Named syntax** (explicit control): ```mlf // Output only @encoding(output: "text/plain") query getText(): string; // Input only @encoding(input: "application/xml") procedure parse(data!: string): result; // Both input and output @encoding(input: "application/cbor", output: "application/json") procedure convert(data!: bytes): object; ``` **Common encoding values:** - `"application/json"` - JSON (default) - `"application/cbor"` - CBOR binary format - `"text/plain"` - Plain text - `"application/xml"` - XML - `"*/*"` - Any MIME type - Custom MIME types as needed ## Annotation Processing Annotations are preserved in the MLF AST and can be accessed by: - Code generators - Linters - Documentation generators - Build tools - Custom processors Each tool decides which annotations to support and how to interpret them. ## Best Practices 1. **Use bare annotations for universal concepts** - Use `@deprecated` without selectors when you want all generators to see it 2. **Use generator selectors for specific tooling** - Use `@rust:derive` or `@typescript:export` when targeting one generator 3. **Group with comma selectors when appropriate** - Use `@rust,typescript:deprecated` to apply the same annotation to multiple generators 4. **Document custom annotations** - Keep a registry of annotations your project uses 5. **Be consistent** - Use the same annotation patterns across your codebase 6. **Don't overuse** - Annotations should augment, not replace, good design ## What's Next? Next, read the [Important Info](/docs/language-guide/important-info/) section to understand critical details about how MLF maps to ATProto Lexicons.