OxL Reference

Step 1. Variables and types

Why#

Most bugs in programs are values that changed in the wrong place at the wrong time. That is why a binding in OxL is immutable by default: let creates a name that cannot be reassigned. If a value must change, you say so with var, and the reader of the code immediately sees which variables are "alive".

Types are static, but you rarely write them: the compiler infers the type from the value. You write types in function signatures and where a value is not obvious (§3).

Example#

oxl✓ checked in CI
fn main() io {
  let name = "Ann"  // let is an immutable binding; the type str is inferred
  var visits = 0  // var is a mutable variable
  visits += 1
  visits += 1
  let ratio: f64 = 2.5  // the type may be written explicitly
  let big = 1_000_000_i64  // suffix and separators in numbers
  let letter = 'x'
  let ok = visits == 2 && !name.isEmpty()
  print("{name} visited {visits} times, ratio {ratio}, big {big}")  // {…} inside a string is interpolation
  print("{letter} {ok} {name.len()} {name.upper()}")
  let (a, b) = (10, 3)  // a tuple and destructuring
  print("{a / b} {a % b} {a as f64 / b as f64}")
  let nothing: i32? = none  // i32? is "a value or none"
  print("{nothing ?? 42}")  // ?? supplies a default
}
Output
Ann visited 2 times, ratio 2.5, big 1000000
x true 3 ANN
3 1 3.3333333333333335
42

Walkthrough#

  • let name = "Ann" - an immutable binding of type str. Strings are UTF-8 text; copying them is cheap.
  • var visits = 0 - a mutable variable of an integer type (the type is inferred). +=, -= and friends require var.
  • 1_000_000_i64 - the _ separator for readability and a type suffix. The main numeric types are i8…i128, u8…u128, usize, f32, f64. Arithmetic checks overflow: +, -, * panic when the range is exceeded instead of silently wrapping (the explicit variants are +% wrapping and +| saturating, §3.1).
  • "{name} visited {visits} times" - any expression can be put in curly braces inside a string.
  • (a, b) is a tuple; let (a, b) = … takes it apart. 10 / 3 is integer division, so for a real result the numbers are converted with as f64.
  • i32? means "i32 or none". none is the only way to say "no value": the language has no null. a ?? b takes a if it is present, otherwise b.

Common mistake: changing a let#

If you try to change an immutable binding, the compiler stops the build and suggests var:

oxl✓ checked in CI
fn main() {
  let count = 0
  count += 1  // let is immutable
}
error
mutate-immutable

Diagnostic: mutate-immutable. The fix is to declare var count = 0.

The second frequent mistake is a type mismatch. OxL does not convert types silently: a string does not go into an i32, and an i32 does not go into an i64 without an explicit as:

oxl✓ checked in CI
fn main() {
  let count: i32 = "five"
}
error
type-mismatch

Diagnostic: type-mismatch. A typo in a name - unresolved-name - is also caught at compile time:

oxl✓ checked in CI
fn main() {
  let total = 10
  print(totl)
}
error
unresolved-name

Remember#

  • let by default; var only where the value really changes.
  • Numbers and strings are never converted into each other implicitly.
  • The absence of a value is the type T?, not a special null; the compiler makes you handle none.

Next: functions.

See also

OxL 1.0.0-beta · Documentation version 1.0.0-beta© OxwaldLink · generated from the Standard, the std sources and the guides