Skip to Content
Cloth

Control flow

Conditions

if and while accept booleans and nullable-reference presence checks. Numbers are not booleans, and non-null references are rejected as always-true conditions.

bool enabled = true; if (enabled) { println("enabled"); } else { println("disabled"); }

Braces are required. To express an additional condition, place another if inside the else block.

While loops

var count = 0; while (count < 3) { println(count); count++; }

The condition runs before each iteration. continue returns to the condition; break exits the innermost loop or switch.

Classical for loops

for (var index = 0; index < 3; index++) { println(index); }

The initializer executes once. Each iteration tests the condition, runs the body, and then runs the updates. Multiple updates are comma-separated and run left to right.

Initializer, condition, and updates are optional. An omitted condition means the loop continues until control exits it. continue runs the updates before rechecking the condition; break skips them and exits. A local declared in the initializer belongs to the loop’s scope.

Array iteration

int32[] values = [1, 2, 3]; for (var value in values) { if (value == 2) { continue; } println(value); }

The array expression is evaluated exactly once. Each iteration loads the next element into a local copy. var infers the element type; an explicit type uses ordinary assignment compatibility. final may qualify the iteration binding.

Reassigning the binding does not replace the element. Index the array to update stored elements. See arrays.

Switch statements

Use switch to select one block from an integer or enum value. It is supported by clothc --check, LLVM/native builds, and Shuttle package compilation.

// Status.co enum { Pending, Running, Done } // Describe.co static func Describe(Status status): string { switch (status) { case Status.Pending, Status.Running: { return "active"; } case Status.Done: { return "done"; } } }

Selectors must be integers or enums. Parentheses around the selector, the colon after each label list, and each arm’s braces are required. A switch needs at least one arm. An optional default: { ... } must appear once, last.

Enum switches must cover every case or include a default, even in void functions and constructors. Integer switches may omit default; unmatched values then continue after the switch. A default remains a possible flow path even when every current enum case is explicitly listed.

Omit default when each new enum case should require a decision at compile time. Use default when future cases should share a fallback. Adding a dependency’s enum case rejects an uncovered switch; removing or renaming a referenced case is still an error even with a default. Shuttle rebuilds affected consumers when enum declarations or imported constants change, including when cases are reordered.

An enum default handles otherwise-unlisted valid cases, not invalid internal tags. Invalid tags trap instead of entering a case or fallback body.

Integer labels accept decimal literals, optionally parenthesized or preceded by one unary minus, if they fit the selector type. Other labels must be qualified cases of the same enum or accessible, verified static final integer/enum constants. Typed integer constants may widen losslessly but cannot narrow just because their value fits. Locals, calls, conversions, arithmetic, ranges, and patterns are not labels. Equal normalized values are duplicates, including 1 and 01, or a case and a constant naming that case.

The selector is evaluated once. Each arm is a separate scope and runs to the end of its block without falling through into the next arm. A trailing break; is optional. break exits the nearest loop or switch; continue skips switches and continues the nearest loop, including a classical for-loop’s updates. Without an enclosing loop, continue is invalid.

Return completeness, required/final field initialization, and nullable-flow checking include early breaks and integer no-match paths. Code after a transfer cannot establish facts for reachable code. A switch allows at most 65,536 value labels and 65,537 arms, counting default.

Return and reachability

return; exits a void function or constructor. return value; exits a value-returning function. Statements after a guaranteed return, break, or continue produce unreachable-code warnings.

A value-returning function cannot fall through. A literal while (true) or a conditionless for with no reachable break does not fall through either.

Loops are limited to these forms. Numeric ranges, destructuring, asynchronous iteration, switch expressions, and pattern matching are not currently supported.