Browse the documentation

AEL 1.0.0

Types

AEL, the Agent Engineering Language, is statically typed: every value has one type, known when you check your code, and no value changes type on its own. Every collection has a fixed capacity, written in its type, so a program's memory use is predictable.

Integers

TypeRange
i8−128 to 127
i16−32,768 to 32,767
i32−2,147,483,648 to 2,147,483,647
i64−9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
u80 to 255
u160 to 65,535
u320 to 4,294,967,295
u640 to 18,446,744,073,709,551,615
  • Each width and sign is its own type. AEL never converts one to another silently: converting is an explicit operation that reports a value out of range (see Errors and checked arithmetic).
  • An integer literal takes the type its place expects, and is an i64 when nothing says otherwise.
  • Arithmetic is checked: an overflow is reported, never silently wrapped.

Booleans and unit

  • bool has the two values true and false. It is not an integer, and an integer is never a condition.
  • (), the unit type, has one value, also written (). It means "no value": a function that returns nothing returns unit.

There is no null value: a value that may be missing has the type Option<T>.

Arrays

[T; N] is a fixed array of exactly N values of type T. Its length is part of its type.

let scores: [i64; 3] = [12, 7, 30];
let best: i64 = scores[2];

Every index is checked against the length. [] is an array of length zero, and needs a type such as [i64; 0] from its context.

Structs

A struct groups named fields. You build one by naming the struct and giving every field, in the order the struct declares them.

struct Point {
    x: i64,
    y: i64,
}

fn total(p: Point) -> i64 {
    return p.x + p.y;
}

fn example() -> i64 {
    let p: Point = Point(2, 3);
    return total(p);
}
  • Fields are read with .: p.x. A field of a let mut binding is assignable: p.x = 5;.
  • A struct cannot contain itself, directly or through its fields.

Enums

An enum is one of several variants, and a variant can carry values.

enum Reply {
    Empty,
    Text(Str<200>),
    Code(u16, Str<40>),
}

fn example() -> Reply {
    return Reply::Code(404, "not found");
}
  • A variant is always written with its enum's name: Reply::Text(…). A variant without values is itself a value: Reply::Empty.
  • You take an enum apart with match, which must handle every variant; see Control flow.

Option and Result

Two enums are built in:

TypeVariantsUse
Option<T>Option::Some(value), Option::NoneA value that may be missing.
Result<T, E>Result::Ok(value), Result::Err(error)The result of anything that can fail. AEL has no exceptions.

Errors and checked arithmetic shows how to return and handle a Result.

Bounded text

Str<N> is UTF-8 text of at most N bytes.

  • A text literal must fit: let name: Str<8> = "a longer name"; fails when you check your code, not when it runs.
  • The length of text is counted in bytes. Reading one byte of text returns a Result, checked against the length.
  • Text has no index operator, no slices and no character indexing: a byte is never taken for a whole character.
  • Adding to text that has no room left returns an error and leaves the text unchanged.

Bounded lists

Vec<T, N> is a list of at most N values of type T.

  • A list starts empty, with room for N values.
  • Adding a value to a full list returns an error that gives the value back, and the list stays unchanged.
  • Removing at an index that does not exist returns an error, and the list stays unchanged.
  • list[i] is checked against the number of values the list holds.

The operations on text and lists belong to the standard library: see Text and collections.

References

&T is a shared reference to a value and &mut T an exclusive one. Ownership describes when you can create and use them.

Type aliases

type gives a type a second name; the two names mean the same type.

type OrderId = u64;
type Lines = Vec<OrderId, 16>;

Equality

== and != compare two integers of the same type, or two booleans. Structs, enums, text and lists are not compared with ==: you compare their parts.

Built-in error types

Four error enums are built in, CapacityError, IndexError, CastError and StringError, for full collections, missing indexes, integer conversions and invalid text. Errors and checked arithmetic lists their variants.

Subagent handles and failures

An agent spawns subagents at run time. Three built-in types describe them:

TypeVariantsUse
Spawned<R>None: the handle is opaque.The handle of one spawned subagent whose result type is R. Your code cannot build or copy one, and join or cancel uses it up.
SpawnRefusalChildrenExhausted, DepthExceeded, ConcurrencyFull, BudgetUnfundable, UnavailableWhy try_spawn could not start a subagent.
SpawnFailureFailed(E), Cancelled, TimedOut, Exhausted, UnknownHow a subagent ended without its result; E is the subagent's declared error.
  • join returns Result<R, SpawnFailure>. When R is itself a Result<T, E>, join returns Result<Result<T, E>, SpawnFailure>: your own result is never merged into the outer one.
  • Their variants are written with their type's name, like every variant: SpawnFailure::TimedOut.

Subagents says what each variant means.

Types across languages

Your application can hold private functions written in other languages, with typed AEL signatures. Functions in other languages lists the types such a signature can use.

Outside this reference

These are outside this reference: floating-point and fixed-point numbers, the machine-sized integers usize and isize, generic types of your own, traits, closures and functions used as values.