A path through Aether, from the first program to actors, servers, C, the
sandbox, the tools and whole programs. Read them in order: each file
teaches one idea, starts by saying what it teaches and how to run it,
and ends with the exact output it prints. CI runs every example that can
run and fails when one stops printing what it says (make check-examples).
Run them from the repository root:
ae run examples/01-language/01-hello.aeWith a toolchain built from this tree, that is ./build/ae run ....
The tutorial walks this path in prose, a chapter
per part below (the first two with the mistakes newcomers make and
exercises); the glossary defines its words. The language
reference is the full story behind each
step; every row below links the part it builds on. Getting
started installs the toolchain.
| Example | What it teaches | Reference |
|---|---|---|
| 01-hello.ae | main, println, interpolation |
Functions |
| 02-values-and-types.ae | numbers, bools, strings, written types, const, module var, as |
Types |
| 03-strings.ae | escapes, comparison by content, std.string, parsing numbers | String literals |
| 04-control-flow.ae | if as a statement and a value, while, for, break, labels |
Control flow |
| 05-functions.ae | typed parameters, defaults, named arguments, several results, clauses with guards | Functions |
| 06-structs.ae | struct values, &p pointers, struct patterns, @derive(eq), using |
Structs |
| 07-enums-and-sums.ae | enum, sum types, exhaustive match |
Sum types |
| 08-match.ae | literals, ranges, strings, list and struct patterns | Match expressions |
| 09-optionals.ae | T?, none, ??, ?., narrowing |
Optionals |
| 10-errors.ae | (value, err), T!, or, !, panic and try |
Result types |
| 11-arrays-and-slices.ae | fixed arrays, slices, .len, checked indexes, make |
Slices |
| 12-collections.ae | std.list, std.map, std.set, sorting | Handles |
| 13-closures.ae | closures, captured variables, fn parameters, trailing blocks |
Closures |
| 14-modules/main.ae | a module of your own, exports, import ... as |
Modules |
| 15-contracts.ae | requires, ensures, where |
Contracts |
| 16-defer.ae | defer, defer try, defer catch |
Defer |
| 17-ownership.ae | what the compiler frees, what the program frees | Memory management |
| Example | What it teaches | Reference |
|---|---|---|
| 01-first-actor.ae | message, actor, receive, spawn, !, wait_for_idle |
Actors |
| 02-state.ae | state an actor keeps between messages | State variables |
| 03-ask-reply.ae | ? and reply, a handle in state |
Ask pattern |
| 04-actor-refs.ae | actor_ref in messages, self, message order |
Sending messages |
| 05-messages.ae | strings, arrays, sequences and handles in messages | Messages |
| 06-pipeline.ae | stages connected by references, items in order | Actor runtime |
| 07-worker-pool.ae | fan-out to workers, fan-in to a collector | Actor runtime |
| 08-state-machine.ae | an enum as state, a transition table | Enums |
| 09-timeouts.ae | receive ... after, a watchdog |
Actor timeouts |
| 10-crash-isolation.ae | a panic stays in its actor, and the others go on | Panics |
| 11-ending.ae | actors.stop, actors.kill, a panic; the reason a monitor's Down carries |
The end of an actor |
| 12-links.ae | actors.link, actors.trap_exits: a failure climbs the links |
Links |
| 13-supervisor.ae | a crashed actor restarted under the same ref, and a supervisor that gives up | Supervisors |
| 14-bounded-mailbox.ae | @mailbox(bound: N), try_send, what a full mailbox does to ! |
A bounded mailbox |
| 15-streams.ae | std.stream: a producer held to a window of credits | std.stream |
| 16-sharded-map.ae | keys split over owner actors instead of a lock | Sharded actor map |
| Example | What it teaches | Reference |
|---|---|---|
| 01-files.ae | std.fs: a temp directory, write, read, walk, remove | stdlib reference |
| 02-json.ae | parse, read by kind, build, encode, errors with a position | stdlib reference |
| 03-processes.ae | run a program with arguments, input, environment; read its output and status | stdlib reference |
| 04-http.ae | an HTTP server and client in one program | HTTP server |
| 05-http-server.ae | a long-running server: middleware, health probes, metrics | HTTP server |
| 06-http2-tls.ae | HTTP/2 and TLS on the same server API | HTTP server |
| 07-reverse-proxy.ae | forwarding to an upstream with std.http.proxy | Reverse proxy |
| 08-proxy-pool.ae | a weighted pool configured as code | Reverse proxy |
| 09-logging.ae | std.log levels, a log file, the threshold | stdlib reference |
| 10-tcp.ae | std.tcp: a server and a client, reading a stream up to where a message ends | std.tcp |
| 11-cryptography.ae | SHA-256, an HMAC, an Ed25519 signature and its check | Cryptography |
Aether compiles to C, so C is one declaration away in both directions. The host examples need that language installed; CI's contrib/host job builds and runs them.
| Example | What it teaches | Reference |
|---|---|---|
| 01-libc.ae | extern, C's types, @extern("c_name") |
The extern keyword |
| 02-own-c/main.ae | @source: a C file the program ships; strings, arrays and structs into C |
@source |
| 03-callbacks.ae | @c_callback: C's qsort calling an Aether comparison |
C callbacks |
| 04-embedding/greeter.ae | --emit=obj: an Aether library in a C program, strings across |
--emit=obj |
| 05-hosts/python.ae | Python run inside the program, under the sandbox | Host modules |
| 05-hosts/lua.ae | Lua run inside the program, under the sandbox | Host modules |
| 05-hosts/quickjs.ae | QuickJS run inside the program, under the sandbox | Host modules |
| 05-hosts/tcl.ae | Tcl run inside the program, under the sandbox | Host modules |
| 05-hosts/go.ae | a Go program as a sandboxed child process | Host modules |
| 05-hosts/tinygo.ae | a Go c-shared library loaded and called in-process | contrib/host/tinygo |
| 06-java-host/aether/manifest.ae | a namespace with a generated Java SDK, called from Java | Embedded namespaces |
| 07-limits/script.ae | a host's limits on the code it runs: a deadline, a cancel, a memory budget | Per-call resource caps |
Code you run does not have to be code you trust. The first three work on every platform; the others need the operating system they name.
| Example | What it teaches | Reference |
|---|---|---|
| 01-sandbox.ae | grant lists, sandbox.enforce, what a refusal looks like |
Containment sandbox |
| 02-trusted-calls.ae | a function the sandboxed block may call with your authority | Containment sandbox |
| 03-audit.ae | std.audit: every check, allowed or refused, read back | Audit trail |
| 04-spawn-sandboxed.ae | a child process held to grants at its libc calls (Linux) | Platform support |
| 05-capsicum.ae | FreeBSD Capsicum: open first, enter, then only what is held |
Capsicum |
| 06-casper.ae | Casper: DNS and sysctl from capability mode (FreeBSD) | Capsicum |
What comes with ae besides run and build.
| Example | What it teaches | Reference |
|---|---|---|
| 01-testing/discount_test.ae | std.spec: describe, it, assertions; ae test |
Testing |
| 02-mutation/mutate.ae | std.mutation: are the tests checking the code? | Mutation testing |
| 03-project/src/main.ae | a project: aether.toml, src/, lib/, ae run <dir> |
Build system |
| 04-lib-path/main.ae | --lib: the module search path and its order |
Module search path |
| 05-ae-help/broken.ae | ae help: what a configuration script got wrong, and the fix |
ae help |
| 05-ae-help/fixed.ae | the same script with the fixes applied | Builder DSL |
Whole programs, each putting several of the ideas above to work.
| Example | What it teaches | Reference |
|---|---|---|
| 01-word-count/main.ae | a file read, split, sorted and counted | stdlib reference |
| 02-chat-room.ae | actors that keep each other in state and broadcast | Actor runtime |
| 03-build-supervisor.ae | os.run_supervised: a deadline, a process group, nothing left behind |
stdlib reference |
| 04-calculator.ae | a terminal UI: a button grid as a DSL, closures over ref cells | Closures and lifetimes |
Classic programming tasks as Rosetta Code
states them, each a whole program whose output CI checks like the rest
(#1191). The lines are counted the same way for each language: a
non-blank line that is not only a comment, for Aether's file here (its
header and Output block left out) and for the first Go and Python
solution in RosettaCodeData
(1d475861, 2026-07-16). The other languages' first files are not always
whole programs (Python's GCD imports fractions.gcd, and its string
reversal is input()[::-1]), so the counts compare length, not like for like.
Over these 25 tasks: Aether 506 lines, Go 770, Python 255.
An example is one .ae file, or a directory when it needs more than one
(a module, a C file). Its header comment says what it teaches and holds:
// Run: <command>, the command a reader types from the repository root:ae run <file or project> [--lib dir] [-- args], anotheraecommand, orsh <script>.// Needs: <what>, when it cannot run unattended: a server that waits for requests, a terminal, a platform, a library. CI builds it, or type-checks it when it imports a contrib bridge, which only that bridge's job builds.// See: <doc>, the part of the documentation it builds on.
A file that belongs to another example (a module it imports) says
// Part of: <that example's file> instead of Run:. An example that
runs unattended ends with its output, exactly:
// Output:
// Hello, Aether!
make check-examples builds every example run with ae run (a warning
fails it), runs the ones with an Output: block and compares, with the
tree's own ae first on PATH, and checks that this README links every
example and nothing else, and that the directory holds only source. Add
the new example's row here, in the place a reader should meet it. The
checker is tests/examples/check.ae.