Syntax
Nexus uses keyword-terminated blocks and mandatory labeled arguments to make program structure unambiguous (see Design). This reference covers all syntactic constructs. For semantic behavior, see Types, Effects, and Semantics.
Comments
// line comment
/* block comment */
Definitions
Functions
Functions are first-class values bound with let:
export let add = fn (a: i64, b: i64) -> i64 do
return a + b
end
export letmakes the function visible to other modules- All arguments are labeled at call sites:
add(a: 1, b: 2) - Generic type parameters:
fn <T>(x: T) -> T do ... end unit-returning functions may omitreturn ()(implicit unit return)
Coeffect/Effect Annotations
let greet = fn (name: string) -> unit require { Logger, Console } do
Logger.log(msg: name)
Console.println(val: name)
return ()
end
Both require { ... } and throws { ... } are optional; omitted means empty row.
External Functions
Foreign function declarations bind a Wasm export to a Nexus name:
export external sin = "sin" : (x: float) -> float
export external length = "array_length" : <T>(arr: &[| T |]) -> i64
Generic externals require explicit type parameters with <T, U, ...>.
Type Definitions
export type Point = { x: float, y: float }
export type Pair<A, B> = { fst: A, snd: B }
export type Result<T, E> = Ok(val: T) | Err(err: E)
Defines either a record type ({ ... }) or a sum type (A(...) | B(...)).
The opaque modifier hides constructors from importers:
export opaque type Handle = Handle(id: i64)
Exception Declarations
export exception NotFound(msg: string)
export exception PermissionDenied(msg: string, code: i64)
Extends the builtin Exn type with new constructors.
Expressions
Literals
| Form | Example | Type |
|---|---|---|
| Integer | 42, -7 |
i64 |
| Float | 3.14, -0.5 |
f64 |
| Char | 'a', '\n', '\t' |
char |
| Boolean | true, false |
bool |
| Unit | () |
unit |
| String | "hello" |
string |
Strings use "..." delimiters with escape sequences. Raw strings use [=[ ... ]=] delimiters (no escape processing).
Supported escape sequences (in both strings and char literals):
| Escape | Value | Description |
|---|---|---|
\0 |
0x00 | Null (octal shorthand) |
\a |
0x07 | Bell |
\b |
0x08 | Backspace |
\t |
0x09 | Tab |
\n |
0x0A | Newline |
\v |
0x0B | Vertical tab |
\f |
0x0C | Form feed |
\r |
0x0D | Carriage return |
\e |
0x1B | Escape (for ANSI sequences) |
\\ |
0x5C | Backslash |
\' |
0x27 | Single quote |
\" |
0x22 | Double quote |
\NNN |
octal | 1–3 octal digits (e.g. \033 = ESC, \177 = DEL) |
\xNN |
hex | Exactly 2 hex digits (e.g. \x1b = ESC, \x41 = ‘A’) |
\u{N..} |
unicode | Variable-length hex codepoint (e.g. \u{1F600}) |
Char literals use single quotes: 'a', '\n', '\x41', '\033'.
Operators
Binary operators with standard precedence (multiplicative binds tighter than additive, comparison, then logical):
| Operators | Domain |
|---|---|
<< >> |
Bit shift (highest) |
* / *. /. & |
Multiplicative / bitwise AND |
+ - +. -. ++ \| ^ |
Additive / string concat / bitwise OR, XOR |
== != < > <= >= |
Integer / generic comparison |
==. !=. <. >. <=. >=. |
Float comparison |
&& |
Logical AND |
\|\| |
Logical OR (lowest) |
Function Calls
add(a: 1, b: 2)
Console.println(val: "hello")
list.map(xs: items, f: transform)
All arguments are labeled. Argument order at the call site does not matter – add(b: 2, a: 1) is equivalent to add(a: 1, b: 2). Port method calls use Port.method(...) syntax.
Lambda Expressions
let f = fn (x: i64) -> i64 do
return x + 1
end
Handler Expressions
let logger = handler Logger require { Console } do
fn info(msg: string) -> unit do
Console.println(val: msg)
return ()
end
end
Match Expression
Match can be used in expression position. Each case body produces a value:
let code = match color do
case Red -> 1
case Green -> 2
case Blue -> 3
end
All case bodies must produce the same type. Cases with return statements diverge and do not contribute to the unified type.
Record and Constructor Expressions
let point = { x: 1.0, y: 2.0 }
let result = Ok(val: 42)
Collection Literals
let xs = [1, 2, 3] // list
let %lxs = %[1, 2, 3] // linear list
let %arr = [| 1, 2, 3 |] // array (linear)
Borrow Expression
let b = &arr // borrow immutable binding
let b2 = &%resource // borrow linear binding
Index and Field Access
let val = (&%arr)[0] // array index
%arr[0] <- 42 // array index assignment
let name = user.name // record field access
Raise Expression
raise NotFound(msg: "key")
Statements
Let Bindings
let x = 10
let name: string = "Nexus"
let ~counter: i64 = 0
let %resource = acquire()
let &view = ~data
Sigils: (none) immutable, ~ mutable, % linear, & borrowed.
Top-level let does not allow ~ or % sigils.
Let-Pattern Destructuring
let { x: x, y: y } = point
let Ok(val: v) = result
Destructures a value directly in a let binding. Desugared to a single-case match during compilation.
Assignment
~x <- 42
Assigns to a mutable binding.
Return
return expr
If-Else
if condition then
// ...
else
// ...
end
The else branch is optional.
Match
match result do
case Ok(val: v) -> process(v: v)
case Err(err: e) -> handle_error(e: e)
end
While Loop
while condition do
// body
end
Evaluates condition before each iteration. If false, exits the loop. Returns unit.
let ~i = 0
while ~i < 10 do
~i <- ~i + 1
end
For Loop
for var = start to end_expr do
// body (var is immutable i64, scoped to body)
end
Iterates var from start (inclusive) to end_expr (exclusive). Both must be i64. The loop variable is immutable within the body and increments by 1 each iteration. Returns unit.
let ~sum = 0
for i = 0 to 5 do
~sum <- ~sum + i
end
// ~sum is 10
Try / Catch
try
risky_operation()
catch err ->
match err do
case NotFound(msg: m) -> ()
case _ -> ()
end
end
Single catch clause binding the Exn value.
Inject
inject stdio.system_handler do
inject console_logger do
program()
end
end
Multiple handlers: inject h1, h2 do ... end.
Conc
conc do
task worker1 do
// ...
end
task worker2 do
// ...
end
end
Patterns
| Pattern | Example | Description |
|---|---|---|
| Literal | 1, true, "hi" |
Matches exact value |
| Variable | x, ~x, %x |
Binds with optional sigil |
| Constructor | Ok(val: v), None |
Destructures variant |
| Record (exact) | { x: p1, y: p2 } |
All fields must match |
| Record (partial) | { x: p1, _ } |
_ must be last; remaining fields ignored |
| Wildcard | _ |
Matches anything, no binding |
Imports
import * as math from "path/to/math.nx" // namespace alias
import { add, sub } from "path/to/math.nx" // named items
import { add as my_add, sub } from "path/to/math.nx" // named with renaming
import { add, sub }, * as math from "path/to/math.nx" // named + namespace
import external "path/to/lib.wasm" // Wasm module
Ports and Handlers
export port Logger do
fn info(msg: string) -> unit
fn warn(msg: string) -> unit
end
let console_logger = handler Logger require { Console } do
fn info(msg: string) -> unit do
Console.println(val: msg)
return ()
end
fn warn(msg: string) -> unit do
Console.println(val: msg)
return ()
end
end
Concurrency
conc do
task name1 do
// ...
end
task name2 do
// ...
end
end
Task names are identifiers. conc blocks wait for all tasks.
Full Syntax (EBNF)
(* ── Top-level ─────────────────────────────────────────────── *)
program ::= top_level*
top_level ::= type_def
| exception_def
| import_def
| port_def
| external_def
| let_def
| comment
(* ── Definitions ───────────────────────────────────────────── *)
type_def ::= [ "export" ] [ "opaque" ] "type" UIDENT [ type_params ] "=" record_type
| [ "export" ] [ "opaque" ] "type" UIDENT [ type_params ] "=" type_sum_def
type_sum_def ::= variant_def ( "|" variant_def )*
variant_def ::= UIDENT [ "(" variant_field ( "," variant_field )* ")" ]
variant_field ::= type | IDENT ":" type
exception_def ::= [ "export" ] "exception" UIDENT [ "(" variant_field ( "," variant_field )* ")" ]
import_def ::= "import" "external" import_path
| "import" "{" import_item ( "," import_item )* "}" [ "," "*" "as" IDENT ] "from" import_path
| "import" "*" "as" IDENT "from" import_path
import_item ::= IDENT [ "as" IDENT ]
import_path ::= STRING_LITERAL
port_def ::= [ "export" ] "port" UIDENT "do" fn_signature* "end"
fn_signature ::= "fn" IDENT param_list "->" type [ "require" throws_type ] [ "throws" throws_type ]
let_def ::= [ "export" ] "let" IDENT [ ":" type ] "=" expr
external_def ::= [ "export" ] "external" IDENT "=" STRING_LITERAL ":" [ type_params ] arrow_type
(* ── Parameters ─────────────────────────────────────────────── *)
type_params ::= "<" UIDENT ( "," UIDENT )* ">"
param_list ::= "(" [ param ( "," param )* ] ")"
param ::= [ sigil ] IDENT ":" type
sigil ::= "~" | "%"
(* ── Types ──────────────────────────────────────────────────── *)
type ::= arrow_type
| generic_type
| primitive_type
| ref_type
| borrow_type
| linear_type
| record_type
| list_type
| array_type
| row_type
| UIDENT (* type variable or monotype *)
primitive_type ::= "i32" | "i64" | "f32" | "f64" | "float" | "bool" | "char" | "string" | "unit"
ref_type ::= "ref" "(" type ")"
borrow_type ::= "&" type
linear_type ::= "%" type
record_type ::= "{" IDENT ":" type ( "," IDENT ":" type )* "}"
list_type ::= "[" type "]"
array_type ::= "[|" type "|]"
generic_type ::= UIDENT "<" type ( "," type )* ">"
row_type ::= "{" type ( "," type )* [ "|" type ] "}"
(* used as require/throws rows *)
arrow_type ::= "(" [ arrow_param ( "," arrow_param )* ] ")"
"->" type [ "require" throws_type ] [ "throws" throws_type ]
arrow_param ::= IDENT ":" type | type
throws_type ::= row_type | generic_type | UIDENT
(* ── Statements ─────────────────────────────────────────────── *)
stmt ::= let_stmt
| return_stmt
| assign_stmt
| if_stmt
| match_stmt
| try_stmt
| inject_stmt
| conc_stmt
| while_stmt
| for_stmt
| comment
| expr_stmt
let_stmt ::= "let" [ sigil ] IDENT [ ":" type ] "=" expr
| "let" pattern "=" expr
return_stmt ::= "return" expr
assign_stmt ::= expr "<-" expr
if_stmt ::= "if" expr "then" stmt* [ "else" stmt* ] "end"
match_stmt ::= "match" expr "do" match_case* "end"
match_case ::= "case" pattern "->" stmt*
try_stmt ::= "try" stmt* "catch" IDENT "->" stmt* "end"
inject_stmt ::= "inject" dotted_ident ( "," dotted_ident )* "do" stmt* "end"
conc_stmt ::= "conc" "do" task_def* "end"
task_def ::= "task" IDENT [ "throws" throws_type ] "do" stmt* "end"
while_stmt ::= "while" expr "do" stmt* "end"
for_stmt ::= "for" IDENT "=" expr "to" expr "do" stmt* "end"
expr_stmt ::= expr
(* ── Expressions (precedence: low → high) ───────────────────── *)
expr ::= expr binary_op expr (* left-associative *)
| match_expr
| postfix_expr
binary_op ::= "||"
| "&&"
| "==" | "!=" | "<=" | ">=" | "<" | ">"
| "==." | "!=." | "<=." | ">=." | "<." | ">."
| "+" | "-" | "++" | "|" | "^"
| "+." | "-."
| "*" | "/" | "&"
| "*." | "/."
| "<<" | ">>"
postfix_expr ::= postfix_expr "." IDENT (* field access *)
| postfix_expr "[" expr "]" (* index *)
| atom_expr
match_expr ::= "match" expr "do" match_case_expr* "end"
match_case_expr ::= "case" pattern "->" expr
atom_expr ::= "(" expr ")"
| raise_expr
| borrow_expr
| lambda_expr
| handler_expr
| call_expr
| constructor_expr
| record_expr
| array_expr
| linear_list_expr
| list_expr
| literal
| variable
raise_expr ::= "raise" expr
borrow_expr ::= "&" [ sigil ] IDENT
lambda_expr ::= "fn" [ type_params ] "(" [ param ( "," param )* ] ")"
"->" type [ "require" throws_type ] [ "throws" throws_type ]
"do" stmt* "end"
handler_expr ::= "handler" UIDENT [ "require" row_type ] "do" handler_fn* "end"
handler_fn ::= "fn" IDENT [ type_params ] "(" [ param ( "," param )* ] ")"
"->" type [ "require" throws_type ] [ "throws" throws_type ]
"do" stmt* "end"
call_expr ::= dotted_ident "(" [ labeled_arg ( "," labeled_arg )* ] ")"
labeled_arg ::= IDENT ":" expr
constructor_expr ::= UIDENT "(" [ ctor_arg ( "," ctor_arg )* ] ")"
ctor_arg ::= [ IDENT ":" ] expr
record_expr ::= "{" [ IDENT ":" expr ( "," IDENT ":" expr )* ] "}"
list_expr ::= "[" [ expr ( "," expr )* [ "," ] ] "]"
linear_list_expr ::= "%" "[" [ expr ( "," expr )* [ "," ] ] "]"
array_expr ::= "[|" [ expr ( "," expr )* [ "," ] ] "|]"
variable ::= [ sigil ] IDENT
dotted_ident ::= IDENT ( "." IDENT )*
(* ── Patterns ───────────────────────────────────────────────── *)
pattern ::= literal_pattern
| constructor_pattern
| record_pattern
| wildcard_pattern
| variable_pattern
literal_pattern ::= literal
variable_pattern ::= [ sigil ] IDENT
constructor_pattern ::= UIDENT "(" [ ctor_pat_arg ( "," ctor_pat_arg )* ] ")"
ctor_pat_arg ::= [ IDENT ":" ] pattern
record_pattern ::= "{" [ rec_pat_field ( "," rec_pat_field )* [ "," ] ] "}"
rec_pat_field ::= "_" | IDENT ":" pattern
(* "_" must be the last element; enables partial matching *)
wildcard_pattern ::= "_"
(* ── Literals ───────────────────────────────────────────────── *)
literal ::= float_literal | integer_literal | boolean_literal
| unit_literal | string_literal | char_literal
float_literal ::= [ "-" ] DIGIT+ "." DIGIT+
integer_literal ::= [ "-" ] DIGIT+
boolean_literal ::= "true" | "false"
unit_literal ::= "()"
string_literal ::= '"' string_char* '"'
| "[=[" raw_char* "]=]"
string_char ::= escape_seq | NON_QUOTE NON_BACKSLASH
raw_char ::= ANY - "]=]"
char_literal ::= "'" char_body "'"
char_body ::= escape_seq | NON_QUOTE NON_BACKSLASH
escape_seq ::= '\' named_esc
| '\' OCTAL_DIGIT OCTAL_DIGIT? OCTAL_DIGIT?
| '\x' HEX_DIGIT HEX_DIGIT
| '\u{' HEX_DIGIT+ '}'
named_esc ::= 'a' | 'b' | 't' | 'n' | 'v' | 'f' | 'r' | 'e'
| '\\' | '\'' | '"'
OCTAL_DIGIT ::= '0'..'7'
HEX_DIGIT ::= '0'..'9' | 'a'..'f' | 'A'..'F'
(* ── Comments & Terminals ───────────────────────────────────── *)
comment ::= line_comment | block_comment
line_comment ::= "//" ANY* ( NEWLINE | EOF )
block_comment ::= "/*" ANY* "*/"
STRING_LITERAL ::= string_literal
(* used in: string values, import paths, external wasm names *)
IDENT ::= [a-z_] [a-zA-Z0-9_]* (* not a keyword *)
UIDENT ::= [A-Z] [a-zA-Z0-9_]* (* constructor names *)
DIGIT ::= [0-9]