Variables & types
Falx is dynamically typed: a variable has no declared type, only whatever value it currently holds.
let and const
Both declare a variable and require an initializer -there's no bare let x;. const just marks the variable immutable; reassigning one is a runtime error.
let count = 0;
const maxRetries = 3;
count = count + 1; // fine, `count` is mutable
maxRetries = 5; // runtime error: cannot assign to a const
Names follow the usual convention: a letter or underscore, then letters, digits or underscores.
The runtime types
These are the values typeof() (see the built-ins reference) can return:
| Type | typeof() | Literal syntax |
|---|---|---|
| Number | "number" | 42, 3.14 -one numeric type; see below |
| String | "string" | "hello" |
| Boolean | "boolean" | true, false |
| Null | "null" | null |
| Array | "array" | [1, 2, 3] |
| Map | "map" | { key: value } |
| Tuple | "tuple" | (1, 2) |
| Function | "function" | define(a, b) { ... } |
| Module | "module" | the result of require(...) |
| Struct instance | the struct's own name, e.g. "Player" | Player(...) |
Numbers
There is one numeric literal form; Falx tracks whether a value is an integer or a float internally (so 2 + 2 stays an integer 4, while 2.0 + 2 becomes the float 4.0), but there is no separate int/float type at the language level -both report typeof as "number".
output(1 + 1); // 2
output(1 + 1.0); // 2.0
output(typeof(1.0)); // number
Only base-10 integer and decimal literals are supported (no hex/octal/binary literals, and no digit separators like 1_000).
Strings, booleans and null
Covered in full on the Strings page. Booleans are exactly true/false (lower-case keywords, not string values). null is its own type and its own keyword.
Truthiness
This is the sharpest edge in the language today -if conditions are checked differently from while conditions, &&/||, and !. This has been verified by running each example below against the interpreter, not just read from source.
if / elseif only ever recognise an actual boolean.
Any condition that isn't literally true or false -a string (even "true"), a number, an array, a map, null -is silently treated as false. No error is raised.
if ("true") { output("shown"); } else { output("not shown"); }
// prints "not shown" -a string is never truthy in `if`, not even "true"
if (1) { output("shown"); } else { output("not shown"); }
// prints "not shown" -same for numbers
while, &&, || and unary ! instead call each value's own boolean conversion directly. Only two types define one:
- Booleans convert to themselves, as expected.
- Strings convert to
trueonly if the string is exactly"true"(case-insensitive); every other string -including"false","", or"hello"-converts tofalse. - Everything else (numbers, arrays, maps, tuples,
null, struct instances) has no boolean conversion at all, and using one in these positions raises a runtime error:Cannot convert '<type>' to bool(orCannot invert '<type>'for!).
let i = 0;
while ("true") { // fine -the string "true" is truthy here
i = i + 1;
if (i == 2) { break; } // `==` produces a real boolean, so `if` sees it fine
}
while (1) { output(i); } // runtime error: Cannot convert 'number' to bool
In practice this means: write conditions that evaluate to an actual boolean -a comparison (i < 10), an equality check (x == true), a call to something like .contains(...) or .isEmpty() (see Arrays / Maps) -rather than handing if or while a raw number, string or array and hoping for JavaScript/Python-style truthiness. None of the example programs in the repository rely on implicit truthiness of non-booleans, which is a good sign this is worth avoiding. See E1011 and E1015 in the errors reference for exactly when this raises.