Items
Items are top-level declarations that define named entities: functions, structs, enums, traits, implementation blocks, and test blocks. Items are hoisted — they are visible throughout the enclosing scope regardless of declaration order.
Functions
function ::= "fn" identifier "(" [ param_list ] ")" [ "->" type_expr ] { contract } block param_list ::= param { "," param } param ::= ["..."] identifier ":" type_expr [ "=" expression ] contract ::= "require" expression [ "," string_literal ] | "ensure" closure [ "," string_literal ]
Functions are named, callable entities with typed parameters, an optional return type, optional contracts, and a body block.
Parameters
Function parameters require type annotations. Parameters may have default values; once a
parameter has a default, all following parameters must also have defaults. The last parameter
may be variadic (prefixed with ...), in which case extra arguments are collected
into an array.
fn greet(name: String, greeting: String = "Hello") -> String { return "${greeting}, ${name}!" } fn sum(...nums: Int) -> Int { return nums.reduce(|a, b| { a + b }, 0) }
Contracts
Functions may declare require (precondition) and ensure
(postcondition) contracts. Require clauses are checked before the function body executes.
Ensure clauses receive the return value and are checked after the body completes.
If a contract fails, a runtime error is raised with the optional message.
fn withdraw(balance: Int, amount: Int) -> Int require amount > 0, "amount must be positive" require amount <= balance, "insufficient funds" ensure |result| { result >= 0 }, "result must be non-negative" { return balance - amount }
Phase-Dependent Dispatch
When a function has phase-annotated parameters (~Type for fluid,
*Type for crystal), the runtime checks the phase of the argument at call
time. Multiple function overloads may exist with different phase constraints, and the
correct one is dispatched based on the argument's phase.
Structs
struct_decl ::= "struct" identifier "{" field_decl { "," field_decl } "}" field_decl ::= identifier ":" type_expr
Structs declare named record types with a fixed set of typed fields. Fields may hold
any value type, including closures. When a closure field has self as its
first parameter, it receives a deep clone of the struct instance when called through
the struct.
struct Point { x: Float, y: Float } struct Counter { count: Int, increment: Fn, get_count: Fn } let c = Counter { count: 0, increment: |self| { self.count + 1 }, get_count: |self| { self.count } }
self receive a deep clone. Mutations to
self inside the closure do not propagate back to the original struct.
Return the modified value to update the caller's copy.
Per-Field Phases
Each struct field carries its own phase tag. Individual fields can be frozen or thawed independently. When the entire struct is frozen, all fields are also frozen.
Enums
enum_decl ::= "enum" identifier "{" variant { "," variant } "}" variant ::= identifier [ "(" type_list ")" ] type_list ::= type_expr { "," type_expr }
Enums declare tagged union types. Each variant has a name and optionally carries a
payload of one or more typed values. Variants are constructed with the ::
syntax and destructured in match expressions.
enum Option { Some(Int), None } enum Shape { Circle(Float), Rectangle(Float, Float), Point } let s = Shape::Circle(5.0) let none = Option::None
Traits
trait_decl ::= "trait" identifier "{" { trait_method } "}" trait_method ::= "fn" identifier "(" [ param_list ] ")" [ "->" type_expr ]
Traits declare a set of method signatures that types can implement. Trait methods are declared without bodies — only their signatures are specified.
trait Printable { fn to_string(self) -> String } trait Comparable { fn compare(self, other: Int) -> Int }
Impl Blocks
impl_block ::= "impl" identifier "for" identifier "{" { function } "}"
Impl blocks provide method implementations for a trait on a specific type. The methods become callable on instances of that type.
impl Printable for Point { fn to_string(self) -> String { return "(${self.x}, ${self.y})" } }
Test Blocks
test_block ::= "test" string_literal block
Test blocks define named test cases. They are only executed when the program is run in test mode. The test name is a string literal for descriptive naming.
test "addition works" { let result = 2 + 2 assert(result == 4, "expected 4") } test "string interpolation" { let name = "Lattice" assert("hi ${name}" == "hi Lattice") }
Lattice