LEVIATHAN v962456e · 962456eee1

Expressions

Strictness

What the compiler rejects instead of guessing, and which failures are left to run time as catchable exceptions.

since 0.1.0-alpha.1linuxwindowswasm

Description

Leviathan prefers a compile error to a guess. A program that names something that does not exist, leaves a generic type argument without a source, or mixes unrelated types in a declaration is rejected before it runs. What the compiler cannot decide ahead of time fails at run time with a RuntimeException that a program can catch.

Failures that surface at run time are catchable

Array<int> xs = [1, 2, 3];
try {
    console.writeln(xs[5]);
} catch (RuntimeException e) {
    console.writeln("array: ${e.message}");
}

try {
    console.writeln(10 / 0);
} catch (RuntimeException e) {
    console.writeln("math: ${e.message}");
}
array: index 5 out of bounds (length 3)
math: division by zero
Map<string, int> m = Map();
try {
    console.writeln(m.at("missing"));
} catch (RuntimeException e) {
    console.writeln("map: ${e.message}");
}

class Plain {
    int v = 1;
}

Plain p = Plain();
try {
    console.writeln(p + p);
} catch (RuntimeException e) {
    console.writeln("operator: ${e.message}");
}
// map: key not found: missing
// operator: no operator '+' on 'Plain'
not run — blocked by an open compiler bug

Rules

  • An unknown name or function is a compile error.
  • A generic construction whose type argument has no source is a compile error. Provide a target type, an argument that carries the type, or an explicit construction tuple such as Box::<int>(). A type argument is inferred when it can be recovered, written explicitly when you want to, and required when it cannot be recovered.
  • Explicit type arguments on a call must match the number of type parameters exactly and are authoritative. A value that conflicts with a type the explicit arguments pin is a compile error at that argument.
  • A prefix operator on the wrong primitive is a compile error: ! outside bool, - outside the numeric types, ~ outside the integer types. See lang.prefix-operators.
  • A value of one kind is not accepted where an unrelated kind is declared: string s = 5;, int y = "5";, bool b = 1; and int n = 1 + "2"; are compile errors.
  • Runtime failures throw a RuntimeException that can be caught: an index out of bounds, at on a map with a missing key, an integer division by zero, a call whose target cannot be resolved, and a binary operator that the left operand's class does not define.

Examples

console.writeln(undefinedName);
// error: unknown name 'undefinedName'

undefinedFunction(3);
// error: unknown function 'undefinedFunction'

class Box<T> { T v; }
Box b = Box();
// error: cannot infer type argument 'T' for 'Box'; provide a target type or a type-bearing argument

string s = 5;
// error: cannot initialize 's' with 'int'
not run — shows a compile error

Providing the type argument explicitly

class Box<T> {
    T value;
}

Box<int> a = Box::<int>(5);
console.writeln(a.value);
5

Notes

Numeric values adopt the declared numeric type at a declaration, so float z = 3; holds 3.0, and conversions between numeric types follow the numeric-conversion rules. Conditions in if and while are meant to be bool; write an explicit comparison such as n != 0 rather than relying on a number as a condition.

See also

  • RuntimeException — The error the standard library throws when an operation fails at run time.
  • Prefix operators — The operand types accepted by the unary operators !, - and ~, and the results they produce.