ROX is a minimal, clarity-first programming language built on a simple belief:
Programming logic should not have to fight the language.
ROX removes implicit behavior, hidden conversions, and syntactic tricks so that expressing logic feels direct and mechanical rather than negotiated.
- We no longer write code that must fit on a punch card — readability wins over brevity.
- Code is read far more than it is written. Optimize for the reader, not the writer.
- Code is written in calm and read in crisis.
- Cleverness is often the enemy of clarity.
- No implicit type conversions
- No bracket indexing (
[]only for list literals) - No exceptions — errors are explicit values (
rox_result[T]) - A single loop construct (
for) - Explicit control flow only
- Strict compile-time type checking
- Reserved Prefix:
roxv26_is reserved for internal namespacing. User variables must not start with this prefix.
ROX automatically namespaces all user-defined identifiers to prevent collisions with C++ keywords and standard library symbols.
- User variable
xbecomesroxv26_xin generated C++. - Reserved Components: The following are NOT namespaced:
mainfunction (entry point)- Built-in functions (
print,isOk, etc.) - Standard library constants (
true,false,none)
Rule: User identifiers must not begin with roxv26_. This prefix is reserved for the compiler.
+,-,*: Standard arithmetic./: Division. Returnsrox_result[T].%: Modulo. Returnsrox_result[T].
==: Equal<,<=,>,>=- Note:
!=is not supported. Usenot (a == b).
andornot: Replaces!and!=. e.g.,not (a == b).
ROX is a statically typed language. All types must be explicit.
-
int64: 64-bit Signed Integer. The default integer type.int64 x = 42; -
float64: 64-bit Floating Point.float64 f = 3.14; -
bool: Boolean (trueorfalse). -
char: Single ASCII character.char c = 'A'; -
string: Immutable sequence of UTF-8 bytes.string s = "Hello"; -
none: Unit type (similar tovoid).
list[T]: Dynamic Array. Ordered collection of elements of typeT.list[int64] numbers = [1, 2, 3];dictionary[K, V]: Hash Map. Key-value pairs.dictionary[string, int64] scores;
User-defined types with named, typed fields.
type User {
id: int64
name: string
}
Construction — all fields are required, using named-field initializers:
User u = User{ id: 7, name: "Taman" };
Field Access & Assignment:
print(u.name); // field access
u.name = "Apon"; // field mutation
Copy Semantics — assignment copies the entire record:
User b = a; // b is an independent copy
Nested Records:
type Address {
city: string
country: string
}
type User {
id: int64
address: Address
}
Functions on Records — no methods, just functions:
function label(User u) -> string {
return u.name;
}
Compile-time errors:
- Missing required field
- Unknown field name
- Duplicate field
- Field type mismatch
- Unknown field access
- Uninitialized record declaration (
User u;)
-
rox_result[T]: Explicit error handling type.rox_result[int64] result = list.at(5); if (isOk(result)) { // Safe to call getValue(result) here int64 val = getValue(result); } else { print("Error: ", getError(result), "\n"); }Safety Note:
getValue(result)can only be called when the result is proven to beok. The compiler enforces this via flow-sensitive analysis.Supported patterns:
- If-Check:
if (isOk(result)) { int64 val = getValue(result); } - Early Return:
if (not(isOk(result))) { return; // or break/continue } // Safe to use here int64 val = getValue(result);
Reassigning
resultinvalidates these checks. - If-Check:
Standard if / else if / else.
if (x > 10) {
print("Big\n");
} else {
print("Small\n");
}
ROX has a single loop construct: for.
// range(start, end, step) - all 3 arguments required
for i in range(0, 5, 1) { ... }
// Backwards
for i in range(10, 0, -1) { ... }
Notes:
- ROX has no unbounded loop construct.
- Every for loop iterates over:
- a finite
range(start, end, step), or - a finite collection (e.g.,
list[T]).
- a finite
rangeis bounded byint64arithmetic. Practical termination is guaranteed for well-formed inputs.
list[int64] nums = [1, 2, 3];
for n in nums {
print(n, " ");
}
The for item in collection form works with any list[T].
break: Terminates the loop.continue: Skips to next iteration.
## Collections
### Lists
Access is strictly checked and returns `rox_result[T]`.
**Methods:**
- `.size() -> int64`
- `.append(item) -> none`
- `.pop() -> none`
- `.at(index) -> rox_result[T]`
### Dictionaries
Key-value maps. Access returns `rox_result[V]`.
**Methods:**
- `.set(key, value) -> none`
- `.remove(key) -> none`
- `.has(key) -> bool`
- `.size() -> int64`
- `.get(key) -> rox_result[V]`
- `.getKeys() -> list[K]`
### Strings
Immutable sequence of bytes.
**Methods:**
- `.size() -> int64`
- `.at(index) -> rox_result[char]`
## Functions
Functions are first-class citizens in ROX. They must specify parameter types and return types explicitly.
```rox
function add(int64 a, int64 b) -> int64 {
return a + b;
}
```
### Syntax
```rox
function name(type name, ...) -> return_type {
// body
return value;
}
```
- **Parameters**: Must have explicit types.
- **Return Type**: Must be explicit. Use `none` if no value is returned.
- **Recursion**: Supported.
- **Type Matching**: Function types must match exactly. No implicit conversions or variance are allowed.
### Return Values
Functions returning `none` can omit the return statement or use `return;` or `return none;`.
```rox
function log(string msg) -> none {
print(msg);
// Implicit return none
}
```
**Rule**: If a function has return type `none`, reaching the end of the function body implicitly returns `none`.
### Functions as Values
Functions can be assigned to variables, passed as arguments, and returned from other functions.
**Syntax for Function Types:** `function(paramType1, paramType2,...) -> returnType`
```rox
function add(int64 a, int64 b) -> int64 {
return a + b;
}
// Assignment
function(int64, int64) -> int64 op = add;
// Passing as argument
function apply(function(int64, int64) -> int64 f, int64 a, int64 b) -> int64 {
return f(a, b);
}
// Returning functions
function log(string msg) -> none {
print(msg);
}
function get_logger() -> function(string) -> none {
return log;
}
function(string)->none logger = get_logger();
logger("Log this message\n");
```
## Built-in Functions
- `print(val...) -> none`: Variadic. Accepts one or more arguments.
- `isOk(rox_result[T]) -> bool`
- `getValue(rox_result[T]) -> T`
- `getError(rox_result[T]) -> string`
- `default(T) -> T`: Returns the zero/empty value for any type.
- `default(int64)` → `0`
- `default(float64)` → `0.0`
- `default(bool)` → `false`
- `default(string)` → `""`
- `default(list[T])` → `[]`
- `default(dict[K,V])` → `{}`
- `default(Record)` → fieldwise defaults (recursive)
- `read_line() -> rox_result[string]`
- Reads one line from standard input.
- The returned string does not include the newline character.
- On end-of-file, returns err(EOF).
- On I/O failure, returns err(<error_message>).
**Example: Echo stdin**
```rox
function main() -> none {
for i in range(0, 1000000, 1) {
rox_result[string] line = read_line();
if (isOk(line)) {
print(getValue(line), "\n");
} else if (getError(line) == EOF) {
break;
} else {
print("error: ", getError(line), "\n");
break;
}
}
}
```
### Built-in Constants
- `pi` (float64)
- `e` (float64)
- `EOF` (string) — error returned by `read_line()` on end of input
## Math Library
### `int64` (64-bit)
- `int64_abs(n)`
- `int64_min(a, b)`
- `int64_max(a, b)`
- `int64_pow(base, exp) -> rox_result[int64]`
### `float64` (double)
- `float64_abs(n)`
- `float64_min(a, b)`
- `float64_max(a, b)`
- `float64_pow(base, exp)`
- `float64_sqrt(n) -> rox_result[float64]`
- `float64_sin, float64_cos, float64_tan`
- `float64_log, float64_exp`
- `float64_floor, float64_ceil`
## Comments
Single-line comments starting with `//`.
```rox
// This is a comment
int64 x = 10;