Skip to main content

require and error handling

The require function allows you to verify Boolean conditions, check for null values, or ensure non-empty collections. For details, see require and require_not_empty in the API reference.

The functions that raise and catch these exceptions are documented together on this page: require and require_not_empty raise them, rell.error raises one unconditionally, and try_call and try_call_catch catch them.

Variants​

Checking Boolean conditions​

  • require(boolean, text): Throws an exception if the boolean argument is false, using the provided text as the error message.

Checking for null values​

  • require(T?, text): T - gives an exception if the argument is null; otherwise returns the argument.
  • require_not_empty(T?, text): T - same as the previous one.

Checking for empty collections​

  • require_not_empty(list<T>, text): list<T> - gives an exception if the argument is an empty list; otherwise it returns the list.
  • require_not_empty(set<T>, text): set<T> - gives an exception if the argument is an empty set; otherwise it returns the set.
  • require_not_empty(map<K,V>, text): map<K,V> - gives an exception if the argument is an empty map; otherwise, it returns the map.

Nullable collections​

  • When passing a nullable collection to require_not_empty, it throws an exception if it is either null or empty.

Example​

function calculate(): integer? = 123;
function get_list(): list<integer> = [1, 2, 3];
function try_to_get_list(): list<integer>? = [4, 5];

val x: integer? = calculate();
val y = require(x, "x is null"); // type of "y" is "integer", not "integer?"
print(y); // 123

val p: list<integer> = get_list();
require_not_empty(p, "List is empty");

val q: list<integer>? = try_to_get_list();
require(q); // fails if q is null
require_not_empty(q); // fails if q is null or an empty list

Error handling with try_call and try_call_catch​

The try_call function catches exceptions and optionally provides fallback values:

val s = "xyz";
val int_or_null = try_call(integer.from_hex(s, *)); // Returns null on failure
val int_or_default = try_call(integer.from_hex(s, *), -1); // Returns -1 on failure
print(int_or_null, int_or_default); // null -1

The try_call_catch function (since 0.14.16) catches only require exceptions and returns a try_call_result<T> that allows inspecting the error message:

function may_fail(): integer {
require(false, "This fails");
return 0;
}

val result = try_call_catch(may_fail(*)); // Returns try_call_result with error info
print(result); // try_call_result{error=This fails}
note

Both functions restore database state on failure – any writes are rolled back. try_call catches all exceptions, while try_call_catch only catches require exceptions and re-throws others.

rell.error​

The rell.error() function (since 0.14.15) fails unconditionally, raising an exception with an optional message:

rell.error(); // Throws an exception with a null message
rell.error('Not allowed.'); // Throws an exception with the message 'Not allowed.'

Unlike require(false, message), rell.error() signals the end of control flow to the compiler, in the same way a return statement does. This lets the compiler narrow types after the call:

function f(x: integer?): integer {
if (x == null) {
rell.error('null argument');
}
return x * x; // The compiler knows that x cannot be null here
}

print(f(5)); // 25