Canonical Syntax Rules
This document is the repository's exact syntax-style reference. It describes what contributors should write in code, tests, examples, and docs.
For design rationale, see language-manifesto.md. For old-to-new mapping, see syntax-migration.md.
Core rule
Prefer one canonical form. Do not present compatibility syntax as an equal alternative.
Variables
Canonical:
let x = 10
let mut count = 0
Not canonical:
let x: Int = 10
If a binding needs an explicit contract, place it in @type:
@type {
count: Int
}
Functions
Canonical:
@type {
add: (Int, Int) -> Int
}
fn add(a, b) {
return a + b
}
Not canonical:
fn add(a: Int, b: Int) -> Int {
return a + b
}
Rules:
- keep parameter names untyped in executable definitions
- keep return flow explicit with
return - put contracts in
@type
Imports
Canonical:
import { login } from services.auth
import { http as nethttp } from std.net
Rules:
- brace imports are the canonical form
- module paths come from file layout
- do not introduce a second canonical import style
@type blocks
Canonical shape everywhere:
@type {
name: Type
}
Supported scopes:
- file/module scope
- class scope
- layer scope
- interface scope
- function scope
Purpose:
- contracts
- interfaces
- explicit local hints where inference needs help
Non-purpose:
- mandatory inline noise on every binding
Class and layer model
Canonical structure:
class User {
let name
layer info {
fn greet() {
return "hello " + name
}
}
@type {
name: String
greet: () -> String
}
}
Rules:
classgroups structurelayergroups related processing or capability functions- do not document or implement alternate structural philosophies as co-equal
Compatibility and strict mode
Compatibility mode still accepts deprecated inline type syntax for migration support.
Strict mode rejects it:
drat build --strict-syntax
drat run --strict-syntax
Strict mode exists to prevent syntax drift. New examples, docs, tests, and migrated self-host code should follow canonical syntax by default.