Browse the documentation

AEL 1.0.0

Results and errors

In AEL, the Agent Engineering Language, a value that may be missing is an Option, and an operation that can fail returns a Result. There is no null value and there are no exceptions: an error is an ordinary typed value that your code handles, passes on or turns into another error.

Option

VariantMeaning
Option::Some(value)A value of type T is present.
Option::NoneNo value. Option::None is itself a value, written without parentheses.

Use Option<T> for a setting that may be left out, a search that may find nothing, or a field that is filled in later.

fn first_positive(values: [i32; 3]) -> Option<i32> {
    let mut i: u32 = 0;
    while i < 3 {
        if values[i] > 0 {
            return Option::Some(values[i]);
        }
        i = i + 1;
    }
    return Option::None;
}

Result

VariantMeaning
Result::Ok(value)The operation succeeded with a value of type T.
Result::Err(error)The operation failed with an error of type E.

An operation that succeeds without a value returns Result<(), E>, and its success is Result::Ok(()).

Handling an error

You take a Result apart with match, which must handle both variants. To pass an error on, return it from your own function; to replace it, return a different error.

enum LookupError {
    NotFound,
    Invalid(u16),
}

fn discount(code: u16) -> Result<u32, LookupError> {
    if code == 0 {
        return Result::Err(LookupError::Invalid(code));
    }
    if code > 500 {
        return Result::Err(LookupError::NotFound);
    }
    return Result::Ok(10);
}

fn price_after(price: u32, code: u16) -> Result<u32, LookupError> {
    match discount(code) {
        Result::Ok(percent) => {
            return Result::Ok(price - price / 100 * percent);
        }
        Result::Err(error) => {
            return Result::Err(error);
        }
    }
}
  • An error type is usually an enum of your own, and its variants can carry values, such as the code in Invalid(u16).
  • Nothing is thrown and nothing is retried unless your code retries it.
  • Binding a value in a match arm moves it out of the matched value; see Ownership.

Built-in error types

Four error types are built in. Each is an enum whose values copy like integers.

TypeVariantsReturned by
CapacityErrorFullAppending text that does not fit.
IndexErrorOutOfBoundsRemoving a list value at an index that does not exist; reading a byte of text past its end.
CastErrorOutOfRangeConverting an integer to a type it does not fit.
StringErrorInvalidUtf8, FullMaking text from bytes that are not valid UTF-8, or do not fit.

Text and collections and Numbers list the operations.

Errors that give your value back

When an operation cannot take a value you pass it, the error hands the value back, so it is never lost and never owned twice:

  • adding to a full list returns Result<(), T>, and the error holds your value;
  • sending a message to a full or closed mailbox returns the message to you; see Concurrency.

In both cases the collection or mailbox stays exactly as it was: nothing is half-added.

Errors from the rest of the library

Operations that reach outside the program return typed results that keep different failures apart, so your code can react to each:

  • Models: an unsupported parameter, input or capability, a missing binding or credential, an input that is too large, an incomplete output, and usage that could not be counted. See Model client.
  • HTTP: a connection or TLS failure, a timeout, a cancellation, a body over its limit. See HTTP client.
  • Programs you run: a non-zero exit, a timeout, a failure to start, output that could not be read, a cancellation, a resource limit. See Files and processes.

When a failure leaves it unknown whether an effect outside the program happened, such as a payment sent before a connection dropped, the result says the effect is uncertain rather than report success or failure. Your code decides how to reconcile it; AEL does not repeat such an operation on its own.

Results of subagents

An agent spawns subagents at run time. What comes back from them is an ordinary typed result:

  • try_spawn returns Result<Spawned<R>, SpawnRefusal>: the handle of the subagent, or the reason it could not start, such as SpawnRefusal::BudgetUnfundable.
  • join returns Result<R, SpawnFailure>: the subagent's result, or how it ended without one, such as SpawnFailure::TimedOut.
  • A subagent's failure is a value that your code handles like any Result::Err. It never fails the code that spawned the subagent by itself.
  • SpawnFailure::Unknown is kept when the subagent's outcome cannot be established: no retry and no new spawn turns it into a success or a failure.

Subagents lists every refusal and failure, and Types the three types.

Errors, panics and diagnostics

KindWhenWhat happens
DiagnosticYou check or build your code.Nothing runs until you fix the source.
Result errorAn operation fails in a way your program expects.The operation returns Result::Err, which you handle.
PanicA defect: an overflow, a division by zero, an index out of range.The program stops with a failure report. A panic is never caught as a Result.

Errors and checked arithmetic describes panics, and Concurrency how agents recover from faults.