Expressions
Lattice is an expression-based language: most constructs produce values. This chapter defines all expression forms, organized by category.
Literal Expressions
literal ::= int_literal | float_literal | string_literal | "true" | "false" | "nil"
Literal expressions evaluate to the value they denote. See Lexical Structure: Literals for syntax details.
Identifier Expressions
An identifier expression evaluates to the value bound to that name in the current scope. If the name is not in scope, a runtime error is raised.
Binary Operators
binary_expr ::= expression binop expression binop ::= "+" | "-" | "*" | "/" | "%" | "==" | "!=" | "<" | ">" | "<=" | ">=" | "&&" | "||" | "&" | "|" | "^" | "<<" | ">>" | "??"
Binary operators follow standard precedence rules (see Appendices: Operator Precedence).
The + operator is overloaded for string concatenation and array concatenation.
The ?? (nil coalesce) operator returns the left operand if it is not nil,
otherwise the right operand.
let sum = 2 + 3 // 5 let greeting = "hi" + " world" // "hi world" let val = nil ?? 42 // 42
Unary Operators
unary_expr ::= unaryop expression unaryop ::= "-" | "!" | "~"
The prefix - negates a numeric value, ! performs logical negation,
and ~ performs bitwise NOT.
Function Calls
call_expr ::= expression "(" [ arg_list ] ")" arg_list ::= expression { "," expression }
A call expression invokes a callable value (function or closure) with a list of arguments. Arguments are evaluated left-to-right and passed by value. If the function defines default parameters, trailing arguments may be omitted.
Method Calls
method_call ::= expression "." identifier "(" [ arg_list ] ")" | expression "?." identifier "(" [ arg_list ] ")"
Method calls invoke type-specific methods on a value. The ?. variant performs
optional chaining: if the receiver is nil, the entire expression evaluates to
nil without calling the method.
Field Access
field_access ::= expression "." identifier | expression "?." identifier
Field access reads a named field from a struct value. The optional chaining variant
?. returns nil if the receiver is nil.
Index Access
index_expr ::= expression "[" expression "]" | expression "?[" expression "]"
Index access reads an element from an array, tuple, or map by index or key. The
?[ variant returns nil instead of raising an error for out-of-bounds
or missing keys.
Array Literals
array_expr ::= "[" [ expression { "," expression } ] "]"
Array literals create a new array value. Elements may include spread expressions.
let a = [1, 2, 3] let b = [0, ...a, 4] // [0, 1, 2, 3, 4]
Struct Literals
struct_lit ::= identifier "{" field_init { "," field_init } "}" field_init ::= identifier ":" expression
Struct literals create an instance of a declared struct by specifying values for each field.
Tuple Literals
tuple_expr ::= "(" expression "," expression { "," expression } ")"
Tuple literals create a fixed-size collection. A trailing comma is required to distinguish
a single-element tuple from a parenthesized expression: (42,) is a tuple,
(42) is just the integer 42.
Range Expressions
range_expr ::= expression ".." expression
Range expressions create a half-open range [start, end). Both operands must
be integers.
Closure Expressions
closure ::= "|" [ closure_params ] "|" ( expression | block ) closure_params ::= closure_param { "," closure_param } closure_param ::= ["..."] identifier ["=" expression]
Closures capture variables from their enclosing scope. Unlike function declarations, closure parameters do not support type annotations — they are bare identifiers. Parameters may have default values, and the last parameter may be variadic.
let add = |a, b| { a + b } let greet = |name, greeting = "hello"| { "${greeting}, ${name}" } let sum_all = |...nums| { nums.reduce(|a, b| { a + b }, 0) }
Spread Expressions
spread_expr ::= "..." expression
The spread operator expands an array's elements inline within an array literal or function call.
If Expressions
if_expr ::= "if" expression block [ "else" ( block | if_expr ) ]
if expressions evaluate a boolean condition and execute the corresponding block.
When used as an expression, both branches must be present and the last expression in each
block becomes the value of the if expression.
let abs = if x >= 0 { x } else { -x }
Match Expressions
match_expr ::= "match" expression "{" { match_arm } "}" match_arm ::= [phase_qual] pattern ["if" expression] "=>" ( expression | block )
Match expressions evaluate a subject and compare it against a series of patterns. The first matching arm's body is evaluated. See Pattern Matching for full details.
For Loops
for_expr ::= "for" identifier "in" expression block
For loops iterate over ranges, arrays, or maps. When iterating over an array, the loop variable receives a deep clone of each element. When iterating over a map, the loop variable is bound to each key as a string. The loop variable is bound fresh for each iteration.
While Loops
while_expr ::= "while" expression block
While loops repeatedly execute the body as long as the condition evaluates to true.
Loop
loop_expr ::= "loop" block
An infinite loop that runs until a break statement is encountered.
Block Expressions
block ::= "{" { statement } [ expression ] "}"
A block is a sequence of statements optionally followed by a trailing expression.
The value of a block is the value of its trailing expression, or Unit if
there is none.
Forge Expressions
forge_expr ::= "forge" block
A forge block provides a controlled mutation context. Variables declared inside the forge block can be mutated freely, and the last expression in the block is the forge's result. Forge blocks are typically used to build complex immutable values through controlled mutation.
fix config = forge { flux m = Map::new() m.set("host", "localhost") m.set("port", 8080) freeze(m) }
Phase Transition Expressions
freeze_expr ::= "freeze" "(" expression ")" [where_clause] [except_clause] thaw_expr ::= "thaw" "(" expression ")" clone_expr ::= "clone" "(" expression ")" anneal_expr ::= "anneal" "(" expression ")" closure sublimate_expr ::= "sublimate" "(" expression ")" crystallize_expr ::= "crystallize" "(" expression ")" block
These expressions transition values between phases. See Phase System.
Scope and Spawn Expressions
scope_expr ::= "scope" block spawn_expr ::= "spawn" block
scope creates a structured concurrency block. All spawn tasks within
a scope must complete before the scope exits. See Concurrency.
Select Expressions
select_expr ::= "select" "{" { select_arm } "}" select_arm ::= identifier "from" expression "=>" block | "default" "=>" block | "timeout" "(" expression ")" "=>" block
Select expressions multiplex across multiple channels. See Concurrency: Select.
Try/Catch Expressions
try_catch ::= "try" block "catch" identifier block
Try/catch catches runtime errors. See Error Handling.
Try-Propagate (?) Expressions
try_propagate ::= expression "?"
The postfix ? operator unwraps a result map. If the value contains an
"err" key, the error is immediately returned from the enclosing function.
Otherwise, the "ok" value is extracted.
Interpolated String Expressions
Double-quoted strings containing ${...} sequences are desugared into a
series of string concatenations at parse time. The inner expressions are evaluated
and converted to strings.
Enum Variant Expressions
enum_variant ::= identifier "::" identifier [ "(" arg_list ")" ]
Creates an enum variant value, optionally with a payload.
Print Expression
print_expr ::= "print" "(" expression { "," expression } ")"
print is a built-in expression that outputs its arguments to standard output
separated by spaces, followed by a newline. It returns Unit.
Lattice