Patterns & Common Errors
Common Patterns
Section titled “Common Patterns”Program Template
Section titled “Program Template”function main() returns ExitCode print("Hello, world!\n") return 0end 'main'Loop With an Early Exit
Section titled “Loop With an Early Exit”function main() returns ExitCode var i = 0 while true 'forever' if i >= 3 'done' break end 'done'
print("{i}\n") i = i + 1 end 'forever'
return 0end 'main'Iterating With an Index
Section titled “Iterating With an Index”function main() returns ExitCode let names = ["ada", "alan", "grace"] for (iter, name) in names.withIterator() 'each' print("{iter.index()}: {name}\n") end 'each'
return 0end 'main'Recursion
Section titled “Recursion”typealias Operand = int(i64.min to i64.max)
function factorial(n Operand) returns Operand if n <= 1 'base' return 1 end 'base'
return n * factorial(n - 1)end 'factorial'
function main() returns ExitCode print("{factorial(5)}\n") // 120 return 0end 'main'Building a String
Section titled “Building a String”String has no +; interpolate, or append in place:
function main() returns ExitCode var csv = "" for n in 1 to 3 'each' csv.append("{n},") end 'each'
print("{csv}\n") // 1,2,3, return 0end 'main'A Lookup With a Fallback
Section titled “A Lookup With a Fallback”typealias Age = int(0 to 150)typealias Ages = Map with (String, Age)
function main() returns ExitCode var ages = Ages.create() ages.upsert("ada", value: 36) let known = try ages.get("ada") otherwise 0 let unknown = try ages.get("bob") otherwise 0 print("{known} {unknown}\n") // 36 0 return 0end 'main'A Factory That Validates
Section titled “A Factory That Validates”typealias Percent = int(0 to 100)
enum PercentError implements Error outOfRangeend 'PercentError'
type Progress export let done as Percent
export static function create(done Percent) returns Self return Self{done: done} end 'create'
export static function parse(text String) returns Self throws PercentError let value = try int.fromString(text) otherwise throw PercentError.outOfRange if value < 0 or value > 100 'range' throw PercentError.outOfRange end 'range'
return Self{done: value as Percent} end 'parse'end 'Progress'
function main() returns ExitCode let p = try Progress.parse("42") otherwise Progress.create(0) print("{p.done}%\n") // 42% return 0end 'main'Common Errors
Section titled “Common Errors”Compile-Time Errors
Section titled “Compile-Time Errors”Each example below is refused with the diagnostic shown.
Type mismatch
let x = 5 + "string" // E2004: Cannot operate on int and StringMissing return
function compute() returns Tally print("working\n")end 'compute' // E3013: missing return statement: 'compute'Assigning to a let
let x = 5x = 10 // E2013: cannot assign to immutable variable: 'x'Self-assignment
x = x // E3067: self-assignment has no effect: 'x = x'A var that never changes
var x = 10return x // E3077: variable 'x' is never reassigned; use 'let' instead of 'var'An unused variable
let unused = 3 // E3012: unused variable: 'unused'Discarding a result
double(5) // E3064: result of pure function 'double' must be usedincrementAndGet() // E3065: result of 'incrementAndGet' is not used (use '_ = expr' to discard)_ = 42 // E3067: expected a function callA missing try
parseDigit("7") // E3057: throwing function requires try: 'parseDigit'Mutating through an immutable name
let items = TallyArray.create()items.push(1) // E3019: cannot pass 'items' to function that mutates parameter 'self'Moving out of a let
let a = Point.create(1)var b = ab.x = 2print("{a.x}\n") // E3102: use of moved value 'a'Sharing a record between a var and a live let
let a = Point.create(1)let b = avar c = a // E3078: cannot assign immutable variable 'a' to mutable binding 'c'; use 'let' instead of 'var', or use clone()Mutating a borrowed collection
var arr = ["hello"]let s = try arr.get(0) otherwise ""arr.push("world") // E3070: cannot mutate 'arr' via 'push' while it is borrowed by 's'print("{s}\n")A closure escaping its frame
function makeAdder(bump Score) returns UnaryOp let f = function(n Score) gives n + bump return f // E3099: cannot return a closure that capturesend 'makeAdder'A bare primitive type
function half(n int) returns int // E3005: Cannot use bare 'int' as a type. Define a typealias with range constraintsMixing typealiases
let bad = score + meters // E3005: operator '+' requires both operands to be the same type: 'Score' and 'Meters' are different typealiases — cast one side with 'as'A mismatched block label
match x 'check' 1 then print("one\n") default then print("other\n")end 'wrong' // E2043: block identifier mismatch: expected 'check', got 'wrong'An empty block
if x > 0 'check'end 'check' // E3082: empty block: 'check'A block holding only a comment is empty too.
A non-exhaustive match
match level 'filter' error then print("error!\n")end 'filter' // E2026: match on enum 'Level' is not exhaustive, missing: trace, infoA redundant loop label
while i < 3 'loop' break 'loop' // E2048: 'break' with label 'loop' targets its own loopend 'loop'Run-Time Behavior
Section titled “Run-Time Behavior”Nothing in Maxon is undefined behaviour. At run time:
| Event | Behavior |
|---|---|
panic("…"), a failed range check, a negative shift count |
the program prints panic at <file>:<line>: <message> and a stack trace to stderr and exits with code 1 |
| an index past the end of a collection | get/set throw ArrayError.indexOutOfBounds, handled with try |
division or mod by zero |
throws DivisionByZero, handled with try |
| integer overflow | wraps around (two’s complement), with no error |
| an allocation never released | exit code 101 |
| a green thread neither awaited nor dropped | exit code 75 |
| a promise consumed through a second read of one array slot | exit code 118 |
| deadlock | exit code 92 |
maxon run and maxon test report these exit codes; see the CLI reference.
Best Practices for AI Agents
Section titled “Best Practices for AI Agents”These rules cover the mistakes code generators make most often when writing Maxon.
-
Label every block and repeat the label on
end.if x > 0 'positive'…end 'positive'. Choose labels that say what the block does. -
Never use bare
intorfloatin a declaration. Declare a typealias named for the purpose and use it for parameters, returns, fields and type arguments:typealias Tally = int(0 to u64.max)typealias TallyArray = Array with Tally -
Pass the first argument positionally and name the rest.
connect("localhost", port: 8080). Naming the first argument is an error. -
Construct records through a static factory.
Point{x: 1}is legal only insidePoint’s own body; elsewhere callPoint.create(1). -
Prefer
let. Avarthat is never reassigned or mutated is an error. Every declared name must be used; write_for one you do not need. -
Handle every throwing call. Write
try call() otherwise <fallback>,otherwise panic("why")when failure is impossible, or a baretryinside a function thatthrowsthe same error type. -
Access collections through methods, with
try. There is noitems[i]:let value = try items.get(index) otherwise 0 -
Guard divisions. Use
try (a / b) otherwise …, or give the divisor a range that excludes zero. -
Build strings with interpolation.
"{name}: {count}"; there is no+onString. -
Match exhaustively. Name every enum or union case; put several on one arm with
or, one per line; usedefault panic("…")ordefault throwsinstead of a plaindefault. -
Cast between typealiases explicitly. Two different aliases never mix; write
value as Target. -
Use
clone()for an independent copy. Assigning a record shares it. -
Keep tests in
*.test.maxonfiles and call every assertion withtry:test 'adds two numbers'try Expect.equal(2 + 2, expected: 4)end 'adds two numbers'