AUP — Aura Patterns
AUP (Aura Uniform Patterns, or simply Aura Patterns) is a catalog of idiomatic solutions to recurring problems, written in canonical Aura. Each pattern is a runnable program under examples/aup/ and is exercised by tests/test_aup.py.
Patterns are conventions, not a library: they use only the language and the standard library. Every example passes aura check and runs.
| # | Pattern | File | Uses |
|---|---|---|---|
| 01 | Option (result-or-null) | examples/aup/option.aura | none, guard, ??, try-as-expression |
| 02 | Builder | examples/aup/builder.aura | let mut, methods returning self |
| 03 | Strategy | examples/aup/strategy.aura | traits → ABCs, polymorphism |
| 04 | Pipeline | examples/aup/pipeline.aura | |>, map/filter/reduce/take |
| 05 | Typed error handling | examples/aup/error_handling.aura | throw, catch Type as e, finally, try expression |
| 06 | Memoization / caching | examples/aup/memoize.aura | @memoize, @cache(maxsize) |
| 07 | Observer | examples/aup/observer.aura | lambdas/closures, dict of callback lists |
| 08 | Resource management | examples/aup/resource.aura | with, enter/exit |
| 09 | Worker pool | examples/aup/worker_pool.aura | stdlib.threading, locks, map_concurrent |
| 10 | Hybrid secure message | examples/aup/hybrid_crypto.aura | stdlib.crypto (ML-KEM, HKDF, HMAC-SHA3) |
01 — Option
Aura has no built-in Option; T | none plus early-exit guard and the coalescing operator ?? cover the same ground without a wrapper type.
def parse_positive(text) -> int | none {
guard text.length() > 0 else { return none }
let value = try { int(text) } catch Error as e { -1 }
guard value > 0 else { return none }
return value
}
let n = parse_positive("42") ?? -1 // fall back on absence
Callers either guard ... != none else { return } or coalesce. Prefer this to returning sentinel values like -1, which are indistinguishable from real data.
02 — Builder
Return self from each configuration method to chain calls. Chainability requires the object to be mutable, so the fields are set with let mut where they change.
let sql = Query("users").select("id", "name").limit(10).build()
03 — Strategy
A trait is an interface that compiles to an abstract base class. Concrete classes extends it, and call sites accept the trait as a parameter type.
trait Discount { public def apply(price: float) -> float }
class PercentOff extends Discount {
private let percent: float = 0.0
public def new(percent: float) { self.percent = percent }
public def apply(price: float) -> float { return price * (1.0 - self.percent / 100.0) }
}
The compiler enforces the contract: a class that omits an abstract method cannot be instantiated.
04 — Pipeline
|> passes the left value as the first argument of the right function. The standard library’s map, filter, reduce, take and drop are data-first, so pipelines read top-to-bottom.
let result = numbers
|> filter((x) => x % 2 == 0)
|> map((x) => x * x)
|> reduce((acc, x) => acc + x, 0)
05 — Typed error handling
throw raises an exception object; catch Type as e handles a specific class. try also works as an expression producing a value.
try {
risky()
} catch ValueError as e {
print(f"bad value: {e}")
} finally {
print("cleanup")
}
let label = try { f"ok: {parse('42')}" } catch Error as e { "failed" }
Rules: try needs at least one catch or a finally; don’t use exceptions for ordinary control flow — use Option (#01) for expected absence.
06 — Memoization
@memoize gives an unbounded cache; @cache(maxsize=N) gives an LRU cache.
@memoize
def fib(n) -> int {
if n < 2 { return n }
return fib(n - 1) + fib(n - 2)
}
Use @memoize for pure functions with a small input domain; use @cache(N) when the domain is large or unbounded.
07 — Observer
Model events as a dictionary from event name to a list of callbacks. Lambdas and named functions are interchangeable as handlers.
bus.on("order", (payload) => print(f"email: {payload}"))
bus.emit("order", 42)
Keep handlers small and side-effect-only; return values are ignored.
08 — Resource management
with guarantees cleanup. A class provides enter() (returns the bound value) and exit(exc_type, exc, tb).
with Timer("load") as t {
print("working...")
}
Nested with blocks run their exit in reverse order (LIFO). Use this for files, connections, locks and timers.
09 — Worker pool
For I/O-bound work, bound concurrency with a thread pool. map_concurrent preserves input order; for manual fan-out, guard shared state with a lock.
let results = threading.map_concurrent((x) => fetch(x), ids, workers: 3)
Warn: Aura globals are shared across threads. Any assignment to shared state from a worker must be protected by a lock (lock.acquire()/release() or with). For CPU-bound work, prefer processes; the GIL limits threading speedups.
10 — Hybrid secure message
Establish a shared secret with a post-quantum KEM (ML-KEM), derive a symmetric key with HKDF, and authenticate with HMAC-SHA3. This is the shape of a hybrid handshake.
let enc = crypto.kem_encapsulate(recipient_public_key)
let key = crypto.hkdf_sha256(enc.shared_secret, 32, info: b"message")
let tag = crypto.hmac_sha3_256(key, message)
Install aura-language[pqc] for a vetted KEM/signature backend. The bundled reference backend is for tests and education only — call crypto.require_production_backend() before protecting real secrets. The XOR encryption in the example is illustrative; use a real AEAD (AES-GCM, ChaCha20-Poly1305) in production.
Conventions
- One spelling. Use canonical syntax (
def,new,none,and,not). See grammar.md. - Immutability by default.
letbinds an immutable name; uselet mutonly when reassignment is genuinely needed. - Early exit with
guard. Validate preconditions at the top of a function. - Traits for contracts,
raisesfor the unexpected. - Prefer the pipeline for data transformations; prefer loops when the body has side effects or early exits.
Adding a pattern
- Add
examples/aup/<name>.aura— a self-contained, runnable program whose first line is// AUP-NN: Title.(two-digit, sequential, no gaps) and which declaresdef main(). - Add a row to the table and a
## NN — Titlesection here explaining when to use it. tests/test_aup.pyruns every example and checks the header, numbering, table row and section stay in sync; no test edit is needed.