Functions and Control Flow
Lux keeps Lua's imperative execution model, but removes the ceremony that makes large GLua files hard to scan. The compiler lowers the extra syntax into direct Lua 5.1 shapes, so the runtime remains predictable.
This page focuses on syntax that changes statement and expression flow.
fn Functions
A fn declaration creates a local function binding. If the body is a single
expression, Lux returns that expression.
Block functions may also end with a tail expression. The tail expression becomes the return value unless it is suppressed with a semicolon.
Top-level function declarations are hoisted across the whole module. That means module parts can call each other's top-level functions without depending on part order.
Only simple top-level fn bindings are hoisted. Top-level non-function locals
are initialized in part order and cannot be used before initialization.
Default Parameters
Default parameters are applied only when the argument is nil. This matches
Lua conventions and avoids treating false as missing.
Defaults are evaluated from left to right, so later defaults may depend on earlier parameters after their defaults have been applied.
GLua Method Declarations
Lux keeps Lua's colon method semantics. A colon method receives self exactly
as Lua would.
When you need a function field without implicit self, use a dot path.
Exported declarations must create module-scope bindings. Method path declarations are not exportable as module bindings; wrap them in a named function when you need public API.
Implicit Return and Semicolons
The final expression in a block is returned. This is intentionally limited to tail position; Lux does not turn every expression statement into a return.
Add a semicolon when the final expression is meant only for side effects.
Use explicit return when you need Lua's multiple-return forwarding.
Conditional Expressions
if can be used as an expression. It lowers to a temporary and ordinary Lua
branching, so each branch runs only when selected.
The short conditional expression is useful in compact value positions. Parenthesize nested forms when readability would otherwise suffer.
Guard Statements
stopif returns early when a condition is true. It is meant for validation and
early exits at the top of functions or loops.
stopifn is the inverted form: stop when the expression is falsey.
Inside loops, stopif may target break or continue.
The exact continue lowering depends on the surrounding loop shape, but the
observable behavior is the same: skip the rest of the current iteration.
Arrow Functions
Arrow functions are expression-friendly anonymous functions. They are useful with array helpers, hooks, and small callbacks.
Use self => when the generated function should receive an explicit self
parameter.
For long callbacks, prefer a named fn; diagnostics and stack traces are easier
to read.
Optional Access
Optional access stops evaluating the chain when the left side is nil. Lux
lowers it through temporaries so complex receivers are evaluated once.
Indexes and call arguments after a failed optional step are not evaluated.
Optional access only protects the steps marked with ?. or ?[]. Ordinary
access after the optional result behaves like ordinary Lua.
?? Nil Coalescing
?? selects the right side only when the left side is nil. Unlike or, it
preserves false.
When mixing ?? with comparisons, group the expression you mean. The compiler
keeps the precedence explicit rather than guessing.
Optional chains compose naturally with ??.
Destructuring Bindings
Object destructuring reads named fields into locals.
Array destructuring reads one-based positions, matching Lua arrays.
Destructuring does not automatically protect against nil receivers. Use ??
or a guard when the source may be absent.
Table Spread
Table spread copies fields from source tables into a new literal. Later entries overwrite earlier entries.
Lux does not deep-clone nested tables. If a nested table must be independent, clone it explicitly with a helper.
Compound Assignment
Compound assignment is syntax for reading, computing, and writing the same place.
Complex left-hand sides are evaluated once. This matters for expressions with function calls or metamethod-heavy indexing.
Pipelines
The pipeline operator passes the left value into an explicit _ placeholder in
the right expression.
The _ placeholder belongs to the immediate pipeline expression. It does not
leak into nested function literals.
If a nested callback needs the piped value, bind it before the callback or pass it as an explicit argument.
do Expressions
do expressions let you compute a value with local statements while staying in
expression position.
Use do when a value needs temporary locals but extracting a named function
would hide nearby context.
Template Strings
Template strings concatenate strings and tostring conversions.
Template strings are not format strings. Use string.format when you need
number formatting or padding.
Multiple Returns and Varargs
Lux preserves Lua's multiple-return rules where they matter: explicit return,
tail calls, and final expression positions.
Local multiple assignment still follows Lua rules. Only the last expression in an expression list can expand to multiple values.
When generated Lua would need extra temporaries, Lux preserves the same observable ordering as the source expression.