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
| Type | Range |
|---|---|
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 |
u8 | 0 to 255 |
u16 | 0 to 65,535 |
u32 | 0 to 4,294,967,295 |
u64 | 0 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
i64when nothing says otherwise. - Arithmetic is checked: an overflow is reported, never silently wrapped.
Booleans and unit
boolhas the two valuestrueandfalse. 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 alet mutbinding 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:
| Type | Variants | Use |
|---|---|---|
Option<T> | Option::Some(value), Option::None | A 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
Nvalues. - 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:
| Type | Variants | Use |
|---|---|---|
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. |
SpawnRefusal | ChildrenExhausted, DepthExceeded, ConcurrencyFull, BudgetUnfundable, Unavailable | Why try_spawn could not start a subagent. |
SpawnFailure | Failed(E), Cancelled, TimedOut, Exhausted, Unknown | How a subagent ended without its result; E is the subagent's declared error. |
joinreturnsResult<R, SpawnFailure>. WhenRis itself aResult<T, E>,joinreturnsResult<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.